How to pass options object to postcss 8 plugin using JS API? - postcss

I wrote an postcss 8 plugin and would like to forward an options object to be used.
The script to run postcss is from the docs [https://github.com/postcss/postcss#js-api]
const postcss = require('postcss')
const resizer = require('../index.js')
const fs = require('fs')
let fileSrc = 'css/src2.css';
let fileDst = 'css/dest.css';
fs.readFile(fileSrc, (err, css) => {
postcss([resizer])
.process(css, { from: fileSrc, to: fileDst}, { factor: 0.75 })
.then(result => {
fs.writeFile(fileDst, result.css, () => true)
if (result.map) {
fs.writeFile(fileDst + '.map', result.map.toString(), () => true)
}
})
})
I tried to use the third argument slot of the .process() method as in the code [postcss.js] but the plugin dones not receive this data
creator.process = function (css, processOpts, pluginOpts) {
postcss version: 8.2.8

Try adding the plugin options in the plugins array instead, like:
postcss([ resizer({ factor: 0.75 }) ])

Related

Next js Sitemap static export

I need help building a Sitemap for my NextJs project.
I build a headless cms using graphql and next, however, everything is statically generated.
I'm having a lot of issues creating a sitemap. I tried using the npm next-sitemap but all the info I find (youtube and forums) are for projects containing "serversideprops", when my project only contains "getStaticProps" and getStaticPaths. In addition to that I also require the map to handle dynamic paths [slug].js. ** I'm not using typescript
Here is what part of my [slug].js looks like:
> graphql query....
>
> export async function getStaticPaths() { const { posts } = await
> graphcms.request(SLUGLIST); return {
> paths: posts.map((post) => ({ params: { slug: post.slug } })),
> fallback: false, }; }
>
> export async function getStaticProps({ params }) { const slug =
> params.slug; const data = await graphcms.request(QUERY, { slug });
> const { posts } = await graphcms.request(QUERY2); const post =
> data.post; return {
> props: {
> post,
> posts,
> }, }; }
thanks everyone!
next-sitemap also creates the routes based on the static pages you generate. The thing is you have to run it after you generated your project.
Typically, you should have a configuration file like this at the base of your project:
next-sitemap.config.js
/** #type {import('next-sitemap').IConfig} */
module.exports = {
siteUrl: process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : 'https://my-fallback-url.com/',
generateRobotsTxt: true,
trailingSlash: true,
targetDirectory: `${__dirname}/public`,
// Wherever are your pages stored
pagesDirectory: `${__dirname}/src/pages`,
};
and on your package.json
"scripts": {
... other configurations
"postbuild": "next-sitemap"
},
Which will trigger next-sitemap after your build is complete. You should then find all the generated xml files containing your sitemap.

Automatic Next-translate i18

I want to do internalisation in my nextJs project with i18 and next-translate.
It works great on my local machine, perhaps when I deploy it it doesn't react correctly.
Indeed in my local machine, it translates automatically when I go to localhost:3000, it redirects automaticly to localhost:3000/fr, but when I deploy it, it doesn't, it stays www.domain.com . On the website deployed I have a langage selector to test, when I click on FR it translates correctly. I don't understand why it doesn't automaticly like when I'm in local machine
When I use :
const { locale, defaultLocale } = useRouter();
on my local machine I have locale = fr (my langage in my explorer is set to french), but on the net it stays en-US
In my code I structured my translation with these methods :
-next.config.js
const nextTranslate = require('next-translate')
module.exports = nextTranslate({
webpack: (config, { isServer, webpack }) => {
return config;
},
i18n: {
localeDetection: true,
},
})
-i18n.json
{
"locales": ["en-US", "fr"],
"defaultLocale": "en-US",
"pages": {
"/": ["index"],
"/services": ["services"],
"/projects": ["projects"],
"/project-dormeuil": ["dormeuil"],
"/theme": ["theme"],
"*": ["layout"]
}
}
I have this hook to keep my translation during the transitions
export default function useTranslationMe(namespace, options) {
const { t, lang } = useTranslation(namespace, options);
const T = useMemo(() => t, [lang]);
return { t: T, lang };
}
In my index file for example I translate my text like so :
let {t, lang} = useTranslationMe('index');
return (
{t('intro-skills-index')}
)

Creating relative URLs with `new URL()` behaves differently when first param is a variable. WHY?

I'm trying to implement web workers in NextJs, I've followed their example but It really bugs me that I cannot pass the worker relative URL as a variable to new URL(url, baseUrl).
The following snippet is where the worker gets called:
import { useEffect, useRef, useCallback } from 'react'
export default function Index() {
const workerRef = useRef()
useEffect(() => {
const workerUrl = '../worker.js';
console.log({
URL: new URL('../worker.js', import.meta.url),
meta: import.meta.url
});
console.log({
URL: new URL(workerUrl, import.meta.url),
meta: import.meta.url
});
workerRef.current = new Worker(new URL('../worker.js', import.meta.url))
workerRef.current.onmessage = (evt) =>
alert(`WebWorker Response => ${evt.data}`)
return () => {
workerRef.current.terminate()
}
}, [])
const handleWork = useCallback(async () => {
workerRef.current.postMessage(100000)
}, [])
return (
<div>
<p>Do work in a WebWorker!</p>
<button onClick={handleWork}>Calculate PI</button>
</div>
)
}
This strangely logs:
{
"URL":"/_next/static/media/worker.3c527896.js",
"meta":"file:///home/omar/CODE/NextJs/lullo/with-web-worker-app/pages/index.js"
}
{
"URL":"file:///home/omar/CODE/NextJs/lullo/with-web-worker-app/worker.js",
"meta":"file:///home/omar/CODE/NextJs/lullo/with-web-worker-app/pages/index.js"
}
How in the world is this any different:
const workerUrl = '../worker.js';
console.log({
URL: new URL('../worker.js', import.meta.url),
meta: import.meta.url
});
console.log({
URL: new URL(workerUrl, import.meta.url),
meta: import.meta.url
});
The problem is that I cannot pass the URL as a prop, to some generic worker caller. I get the annoying error:
SecurityError: Failed to construct 'Worker': Script at 'file:///home/omar/CODE/NextJs/lullo/client/src/utils/WebWorkers/postErrorToServer.ts' cannot be accessed from origin 'http://localhost:3000'.
This is probably happening because in the first case:
const workerUrl = '../worker.js';
const url = new URL(workerUrl, import.meta.url);
webpack sees the URL as dynamic and is unable to properly bundle the web worker at compile-time. Something similar happens if you define the worker as follows:
const url = new URL('../worker.js', import.meta.url);
const worker = new Worker(url);
This comment on a discussion in webpack's GitHub repo might help in your case. I don't think the worker URL can be truly dynamic, due to the above reason - webpack needs to know the url of the worker script at compile-time.

Load different JS library files for different components

I have a website made in ReactJS. In public/index.html, I have
<head>
<script src="/lib/analyzejs-v1.js"></script>
<script src="/lib/analyzejs-v2.js"></script>
</head>
<body>
<div id="root"></div>
</body>
where analyzejs-v1.js has 6Mo, and analyzejs-v2.js has 3Mo; they are all fixed files that I could not much modify.
These two files are not modules; their functions are declared (e.g., declare function f1(address: string): string; in src/defines/analyzejs-v1.d.ts). So some components call functions of analyzejs-v1.js by using a function name like f1(...) directly without any namespace, import, or export. And the rest of the components call functions of analyzejs-v2.js by using a function name like f2(...) directly without any namespace, import, or export.
It takes time to load these two js library files. So I'm looking for a way to load either analyzejs-v1.js or analyzejs-v2.js according to the component (or URL).
So does anyone know a conventional way to load different JS library files for different components?
If you don't need two script at the same time, you can add the script tag in the runtime when necessary.
I can provide you a hook which I have used to load the script on the fly.
export function useScript(url: string, clean: boolean = false, cleanJob: () => void = () => undefined): boolean {
const [loaded, setLoaded] = useState(false);
useEffect(() => {
let create = false;
let script = document.querySelector(`script[src="${url}"]`) as HTMLScriptElement | null;
if (!script) {
script = document.createElement('script');
script.src = url;
script.async = true;
if (type (document as any).attachEvent === 'object') {
(script as any).onreadystatechange = () => {
if ((script as any).readyState === 'loaded') {
setLoaded(true);
}
}
} else {
script.onload = () => {
setLoaded(true);
}
}
document.body.appendChild(script);
create = true;
} else {
setLoaded(true);
}
// For a special library, you can do the clean work by deleting the variable it exports.
return () => {
if (create && script && clean) {
setLoaded(false);
document.body.removeChild(script);
cleanJob && cleanJob();
}
}
}, [url]);
return loaded;
}
To use it:
export const Comp = (props: ICompProps) => {
const loaded = useScript('https://path/to/the/script.js');
// if you want to do some clean work, Suppose the external script introduces the variable A, And A can be reasigned.
// const loaded = useScript('https://path/to/the/script.js', true, () -> { A = undefined; });
useEffect(() -> {
if (loaded) {
// Suppose the external script introduces the variable A. Now it is available.
const a = new A();
// do something with a.
}
}, [loaded]);
if (loaded) {
return XXX;
} else {
return null;
}
}
If the script is not a module, just add a typescript declare file without import statements, and declare the global variable the script export. Such as:
declare interface XXX {
YYY
}
declare const ScriptValue: XXX;
When you import a script using the <script> tag, the library can only be used client side and therefore not with node. However, if you mark it as a module, another script can use it like this:
index.html:
<script src="test.mjs" type="module"></script>
<script type="module">
import {hello} from "./test.mjs"
hello()
</script>
test.mjs:
export function hello(text) {
console.log("hello from test")
}
The only thing is the communication between your react scripts and that inline script. The only way, I figured out, how to achieve this is using window.
DISCLAIMER
I'm really not sure, if anyone should use it this way. I have only tested this once and it might very well break... Maybe someone else can tell me their opinion on my approach.
index.tsx
... // imports
(window as any).importStuff = (a: any) => {
a.hello()
}
...
index.html
<script src="test.mjs" type="module"></script>
<script type="module">
import {hello} from "./test.mjs"
window.importStuff({
hello: hello
})
</script>
If you are looking for better performance of the page and not let the scripts block the DOM content loading, then you may want to look at adding defer to the script https://javascript.info/script-async-defer#defer.
If you are looking for dynamic loading of script, and load the script only when its first used, check this doc https://javascript.info/script-async-defer#dynamic-scripts
You can create <script> tag when your component is loaded.
At the first, We create a function to create the script tag
const scriptGenerator = (options = {}) => {
const s = document.createElement("script");
for (const option in options) {
s[option] = options[option]
}
document.querySelector("head").appendChild(s);
}
We can use two attributes to load scripts
defer
async
defer: The defer attribute tells the browser not to wait for the script. Instead, the browser will continue to process the HTML, build DOM. The script loads “in the background”, and then runs when the DOM is fully built.
async: The async attribute is somewhat like defer. It also makes the script non-blocking. But it has important differences in the behavior.
async & defer Docs
After all these steps, we can define our script in the head. You can use useEffect
useEffect(() => {
// V1
scriptGenerator({
src: "...",
async: 1,
});
// V2
scriptGenerator({
src: "...",
async: 1,
});
}, []);
Do not forget to delete them after exiting the component
useEffect(() => {
// Generate script
return () => {
document
.querySelector("head")
.querySelectorAll('script[src="..."]')
.remove();
}
});
If we want to reach a conclusion, it is as follows:
import { useEffect } from 'react';
const Component = () => {
const scriptGenerator = (options = {}) => {
const s = document.createElement("script");
for (const option in options) {
s[option] = options[option]
}
document.querySelector("head").appendChild(s);
}
useEffect(() => {
// V1
scriptGenerator({
src: "...",
async: 1,
});
// V2
scriptGenerator({
src: "...",
async: 1,
});
return () => {
document
.querySelector("head")
.querySelectorAll('script[src="..."]')
.remove();
}
});
}

React - Import multiple images in given directory

const importAll = require =>
require.keys().reduce((acc, next) => {
acc[next.replace("./", "")] = require(next);
return acc;
}, {});
const images = importAll(
require.context('./images', false, /\.(png|jpe?g|svg)$/)
);
console.log(images);
I have the above block of code that imports all files in a given directory given that they end in the required type however, I want to reuse this code so that I can import it into different modules across my app and specify the path for each individual module.
I've also tried the below block of code but am new to this syntax, any help is appreciated!
const importAll = require =>
require.keys().reduce((acc, next) => {
acc[next.replace("./", "")] = require(next);
return acc;
}, {});
const images = (path) => importAll(
require.context(path, false, /\.(png|jpe?g|svg)$/)
);
export default {
images,
}
Found out that It's because require.context will only take a literal regex in webpack 4 and therefore, not a variable.
See here https://webpack.docschina.org/guides/dependency-management/
However, mcm-ham found a work around which can be viewed here https://github.com/webpack/webpack/issues/4772?fbclid=IwAR3m6kmghtm4EoV8IspcqEfFItdeMnzQHGiEkXbwqgOywXSUFxcWXxbyjRI#issuecomment-344556837

Resources