Initial boiler plate project
This commit is contained in:
248
node_modules/next/dist/esm/build/webpack/loaders/next-barrel-loader.js
generated
vendored
Normal file
248
node_modules/next/dist/esm/build/webpack/loaders/next-barrel-loader.js
generated
vendored
Normal file
@ -0,0 +1,248 @@
|
||||
/**
|
||||
* ## Barrel Optimizations
|
||||
*
|
||||
* This loader is used to optimize the imports of "barrel" files that have many
|
||||
* re-exports. Currently, both Node.js and Webpack have to enter all of these
|
||||
* submodules even if we only need a few of them.
|
||||
*
|
||||
* For example, say a file `foo.js` with the following contents:
|
||||
*
|
||||
* export { a } from './a'
|
||||
* export { b } from './b'
|
||||
* export { c } from './c'
|
||||
* ...
|
||||
*
|
||||
* If the user imports `a` only, this loader will accept the `names` option to
|
||||
* be `['a']`. Then, it request the "__barrel_transform__" SWC transform to load
|
||||
* `foo.js` and receive the following output:
|
||||
*
|
||||
* export const __next_private_export_map__ = '[["a","./a","a"],["b","./b","b"],["c","./c","c"],...]'
|
||||
*
|
||||
* format: '["<imported identifier>", "<import path>", "<exported name>"]'
|
||||
* e.g.: import { a as b } from './module-a' => '["b", "./module-a", "a"]'
|
||||
*
|
||||
* The export map, generated by SWC, is a JSON that represents the exports of
|
||||
* that module, their original file, and their original name (since you can do
|
||||
* `export { a as b }`).
|
||||
*
|
||||
* Then, this loader can safely remove all the exports that are not needed and
|
||||
* re-export the ones from `names`:
|
||||
*
|
||||
* export { a } from './a'
|
||||
*
|
||||
* That's the basic situation and also the happy path.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ## Wildcard Exports
|
||||
*
|
||||
* For wildcard exports (e.g. `export * from './a'`), it becomes a bit more complicated.
|
||||
* Say `foo.js` with the following contents:
|
||||
*
|
||||
* export * from './a'
|
||||
* export * from './b'
|
||||
* export * from './c'
|
||||
* ...
|
||||
*
|
||||
* If the user imports `bar` from it, SWC can never know which files are going to be
|
||||
* exporting `bar`. So, we have to keep all the wildcard exports and do the same
|
||||
* process recursively. This loader will return the following output:
|
||||
*
|
||||
* export * from '__barrel_optimize__?names=bar&wildcard!=!./a'
|
||||
* export * from '__barrel_optimize__?names=bar&wildcard!=!./b'
|
||||
* export * from '__barrel_optimize__?names=bar&wildcard!=!./c'
|
||||
* ...
|
||||
*
|
||||
* The "!=!" tells Webpack to use the same loader to process './a', './b', and './c'.
|
||||
* After the recursive process, the "inner loaders" will either return an empty string
|
||||
* or:
|
||||
*
|
||||
* export * from './target'
|
||||
*
|
||||
* Where `target` is the file that exports `bar`.
|
||||
*
|
||||
*
|
||||
*
|
||||
* ## Non-Barrel Files
|
||||
*
|
||||
* If the file is not a barrel, we can't apply any optimizations. That's because
|
||||
* we can't easily remove things from the file. For example, say `foo.js` with:
|
||||
*
|
||||
* const v = 1
|
||||
* export function b () {
|
||||
* return v
|
||||
* }
|
||||
*
|
||||
* If the user imports `b` only, we can't remove the `const v = 1` even though
|
||||
* the file is side-effect free. In these caes, this loader will simply re-export
|
||||
* `foo.js`:
|
||||
*
|
||||
* export * from './foo'
|
||||
*
|
||||
* Besides these cases, this loader also carefully handles the module cache so
|
||||
* SWC won't analyze the same file twice, and no instance of the same file will
|
||||
* be accidentally created as different instances.
|
||||
*/ import path from "path";
|
||||
import { transform } from "../../swc";
|
||||
// This is a in-memory cache for the mapping of barrel exports. This only applies
|
||||
// to the packages that we optimize. It will never change (e.g. upgrading packages)
|
||||
// during the lifetime of the server so we can safely cache it.
|
||||
// There is also no need to collect the cache for the same reason.
|
||||
const barrelTransformMappingCache = new Map();
|
||||
async function getBarrelMapping(resourcePath, swcCacheDir, resolve, fs) {
|
||||
if (barrelTransformMappingCache.has(resourcePath)) {
|
||||
return barrelTransformMappingCache.get(resourcePath);
|
||||
}
|
||||
// This is a SWC transform specifically for `optimizeBarrelExports`. We don't
|
||||
// care about other things but the export map only.
|
||||
async function transpileSource(filename, source, isWildcard) {
|
||||
const isTypeScript = filename.endsWith(".ts") || filename.endsWith(".tsx");
|
||||
return new Promise((res)=>transform(source, {
|
||||
filename,
|
||||
inputSourceMap: undefined,
|
||||
sourceFileName: filename,
|
||||
optimizeBarrelExports: {
|
||||
wildcard: isWildcard
|
||||
},
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: isTypeScript ? "typescript" : "ecmascript",
|
||||
[isTypeScript ? "tsx" : "jsx"]: true
|
||||
},
|
||||
experimental: {
|
||||
cacheRoot: swcCacheDir
|
||||
}
|
||||
}
|
||||
}).then((output)=>{
|
||||
res(output.code);
|
||||
}));
|
||||
}
|
||||
// Avoid circular `export *` dependencies
|
||||
const visited = new Set();
|
||||
async function getMatches(file, isWildcard, isClientEntry) {
|
||||
if (visited.has(file)) {
|
||||
return null;
|
||||
}
|
||||
visited.add(file);
|
||||
const source = await new Promise((res, rej)=>{
|
||||
fs.readFile(file, (err, data)=>{
|
||||
if (err || data === undefined) {
|
||||
rej(err);
|
||||
} else {
|
||||
res(data.toString());
|
||||
}
|
||||
});
|
||||
});
|
||||
const output = await transpileSource(file, source, isWildcard);
|
||||
const matches = output.match(/^([^]*)export (const|var) __next_private_export_map__ = ('[^']+'|"[^"]+")/);
|
||||
if (!matches) {
|
||||
return null;
|
||||
}
|
||||
const matchedDirectives = output.match(/^([^]*)export (const|var) __next_private_directive_list__ = '([^']+)'/);
|
||||
const directiveList = matchedDirectives ? JSON.parse(matchedDirectives[3]) : [];
|
||||
// "use client" in barrel files has to be transferred to the target file.
|
||||
isClientEntry = directiveList.includes("use client");
|
||||
let exportList = JSON.parse(matches[3].slice(1, -1));
|
||||
const wildcardExports = [
|
||||
...output.matchAll(/export \* from "([^"]+)"/g)
|
||||
].map((match)=>match[1]);
|
||||
// In the wildcard case, if the value is exported from another file, we
|
||||
// redirect to that file (decl[0]). Otherwise, export from the current
|
||||
// file itself.
|
||||
if (isWildcard) {
|
||||
for (const decl of exportList){
|
||||
decl[1] = file;
|
||||
decl[2] = decl[0];
|
||||
}
|
||||
}
|
||||
// This recursively handles the wildcard exports (e.g. `export * from './a'`)
|
||||
if (wildcardExports.length) {
|
||||
await Promise.all(wildcardExports.map(async (req)=>{
|
||||
const targetPath = await resolve(path.dirname(file), req.replace("__barrel_optimize__?names=__PLACEHOLDER__!=!", ""));
|
||||
const targetMatches = await getMatches(targetPath, true, isClientEntry);
|
||||
if (targetMatches) {
|
||||
// Merge the export list
|
||||
exportList = exportList.concat(targetMatches.exportList);
|
||||
}
|
||||
}));
|
||||
}
|
||||
return {
|
||||
exportList,
|
||||
wildcardExports,
|
||||
isClientEntry
|
||||
};
|
||||
}
|
||||
const res = await getMatches(resourcePath, false, false);
|
||||
barrelTransformMappingCache.set(resourcePath, res);
|
||||
return res;
|
||||
}
|
||||
const NextBarrelLoader = async function() {
|
||||
this.async();
|
||||
this.cacheable(true);
|
||||
const { names, swcCacheDir } = this.getOptions();
|
||||
// For barrel optimizations, we always prefer the "module" field over the
|
||||
// "main" field because ESM handling is more robust with better tree-shaking.
|
||||
const resolve = this.getResolve({
|
||||
mainFields: [
|
||||
"module",
|
||||
"main"
|
||||
]
|
||||
});
|
||||
const mapping = await getBarrelMapping(this.resourcePath, swcCacheDir, resolve, this.fs);
|
||||
// `resolve` adds all sub-paths to the dependency graph. However, we already
|
||||
// cached the mapping and we assume them to not change. So, we can safely
|
||||
// clear the dependencies here to avoid unnecessary watchers which turned out
|
||||
// to be very expensive.
|
||||
this.clearDependencies();
|
||||
if (!mapping) {
|
||||
// This file isn't a barrel and we can't apply any optimizations. Let's re-export everything.
|
||||
// Since this loader accepts `names` and the request is keyed with `names`, we can't simply
|
||||
// return the original source here. That will create these imports with different names as
|
||||
// different modules instances.
|
||||
this.callback(null, `export * from ${JSON.stringify(this.resourcePath)}`);
|
||||
return;
|
||||
}
|
||||
const exportList = mapping.exportList;
|
||||
const isClientEntry = mapping.isClientEntry;
|
||||
const exportMap = new Map();
|
||||
for (const [name, filePath, orig] of exportList){
|
||||
exportMap.set(name, [
|
||||
filePath,
|
||||
orig
|
||||
]);
|
||||
}
|
||||
let output = "";
|
||||
let missedNames = [];
|
||||
for (const name of names){
|
||||
// If the name matches
|
||||
if (exportMap.has(name)) {
|
||||
const decl = exportMap.get(name);
|
||||
if (decl[1] === "*") {
|
||||
output += `\nexport * as ${name} from ${JSON.stringify(decl[0])}`;
|
||||
} else if (decl[1] === "default") {
|
||||
output += `\nexport { default as ${name} } from ${JSON.stringify(decl[0])}`;
|
||||
} else if (decl[1] === name) {
|
||||
output += `\nexport { ${name} } from ${JSON.stringify(decl[0])}`;
|
||||
} else {
|
||||
output += `\nexport { ${decl[1]} as ${name} } from ${JSON.stringify(decl[0])}`;
|
||||
}
|
||||
} else {
|
||||
missedNames.push(name);
|
||||
}
|
||||
}
|
||||
// These are from wildcard exports.
|
||||
if (missedNames.length > 0) {
|
||||
for (const req of mapping.wildcardExports){
|
||||
output += `\nexport * from ${JSON.stringify(req.replace("__PLACEHOLDER__", missedNames.join(",") + "&wildcard"))}`;
|
||||
}
|
||||
}
|
||||
// When it has `"use client"` inherited from its barrel files, we need to
|
||||
// prefix it to this target file as well.
|
||||
if (isClientEntry) {
|
||||
output = `"use client";\n${output}`;
|
||||
}
|
||||
this.callback(null, output);
|
||||
};
|
||||
export default NextBarrelLoader;
|
||||
|
||||
//# sourceMappingURL=next-barrel-loader.js.map
|
||||
Reference in New Issue
Block a user