@webiny/build-tools 0.0.0-unstable.61c048f412

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/.babelrc.js +1 -0
  2. package/.eslintrc.cjs +6 -0
  3. package/LICENSE +21 -0
  4. package/README.md +11 -0
  5. package/bundling/admin/createBuildAdmin.js +17 -0
  6. package/bundling/admin/createRsbuildConfig.js +142 -0
  7. package/bundling/admin/createWatchAdmin.js +14 -0
  8. package/bundling/admin/index.js +3 -0
  9. package/bundling/function/createBuildFunction.js +17 -0
  10. package/bundling/function/createRsbuildConfig.js +84 -0
  11. package/bundling/function/createWatchFunction.js +14 -0
  12. package/bundling/function/index.js +3 -0
  13. package/bundling/importValidatorPlugin.js +82 -0
  14. package/bundling/printBuildStats.js +44 -0
  15. package/index.d.ts +82 -0
  16. package/index.js +8 -0
  17. package/package.json +93 -0
  18. package/packages/buildPackage/babelCompile.js +103 -0
  19. package/packages/buildPackage/copyToDist.js +11 -0
  20. package/packages/buildPackage/tsAliasReplacer.js +39 -0
  21. package/packages/buildPackage/tsCompile.js +69 -0
  22. package/packages/buildPackage/validateEsmImports.js +91 -0
  23. package/packages/buildPackage.js +40 -0
  24. package/packages/createBabelConfigForNode.js +19 -0
  25. package/packages/createBabelConfigForReact.js +20 -0
  26. package/packages/createBuildPackage.js +7 -0
  27. package/packages/createSwcConfigForNode.js +45 -0
  28. package/packages/createWatchPackage.js +7 -0
  29. package/packages/index.js +6 -0
  30. package/packages/watchPackage.js +117 -0
  31. package/traverseLoaders.js +14 -0
  32. package/utils/PackageJson.backup.ts +46 -0
  33. package/utils/PackageJson.d.ts +33 -0
  34. package/utils/PackageJson.js +44 -0
  35. package/utils.js +33 -0
  36. package/workspaces/index.js +12 -0
  37. package/workspaces/linkWorkspaces.js +107 -0
@@ -0,0 +1,103 @@
1
+ import fs from "fs";
2
+ import { dirname, extname, join, parse, relative } from "path";
3
+ import * as babel from "@babel/core";
4
+ import glob from "fast-glob";
5
+
6
+ const BABEL_COMPILE_EXTENSIONS = [".js", ".jsx", ".ts", ".tsx"];
7
+ const BABEL_SKIP_EXTENSIONS = [".d.ts"];
8
+
9
+ const withSourceMapUrl = (file, code) => {
10
+ const { name } = parse(file);
11
+ return [code, "", `//# sourceMappingURL=${name}.js.map`].join("\n");
12
+ };
13
+
14
+ const getDistCopyFilePath = ({ file, cwd }) => {
15
+ const relativeDir = relative(cwd, file);
16
+ return join(cwd, relativeDir.replace("src", "dist"));
17
+ };
18
+
19
+ const getDistFilePaths = ({ file, cwd }) => {
20
+ const { dir, name } = parse(file);
21
+
22
+ const relativeDir = relative(cwd, dir);
23
+
24
+ const code = join(cwd, relativeDir.replace("src", "dist"), name + ".js");
25
+ const map = join(cwd, relativeDir.replace("src", "dist"), name + ".js.map");
26
+ return { code, map };
27
+ };
28
+
29
+ export const babelCompile = async ({ cwd }) => {
30
+ // We're passing "*.*" just because we want to copy all files that cannot be compiled.
31
+ // We want to have the same behaviour that the Babel CLI's "--copy-files" flag provides.
32
+ const pattern = join(cwd, "src/**/*.*").replace(/\\/g, "/");
33
+ const files = glob.sync(pattern, {
34
+ onlyFiles: true,
35
+ dot: true
36
+ });
37
+
38
+ const compilations = [];
39
+ const copies = [];
40
+
41
+ for (let i = 0; i < files.length; i++) {
42
+ const file = files[i];
43
+ if (
44
+ fileEndsWith(file, BABEL_COMPILE_EXTENSIONS) &&
45
+ !fileEndsWith(file, BABEL_SKIP_EXTENSIONS)
46
+ ) {
47
+ compilations.push(
48
+ babel
49
+ .transformFileAsync(file, {
50
+ cwd,
51
+ sourceMaps: true
52
+ })
53
+ .then(results => [file, results])
54
+ );
55
+ continue;
56
+ }
57
+
58
+ // All other files should be copied as-is.
59
+ copies.push(copyFile(file, cwd));
60
+ }
61
+
62
+ // At this point, just wait for compilations to be completed, so we can proceed with writing the files ASAP.
63
+ await Promise.all(compilations);
64
+
65
+ const writes = [];
66
+ for (let i = 0; i < compilations.length; i++) {
67
+ const [file, result] = await compilations[i];
68
+ const { code, map } = result;
69
+
70
+ const paths = getDistFilePaths({ file, cwd });
71
+ fs.mkdirSync(dirname(paths.code), { recursive: true });
72
+
73
+ // Save the compiled JS file.
74
+ writes.push(fs.promises.writeFile(paths.code, withSourceMapUrl(file, code), "utf8"));
75
+
76
+ // Save source maps file.
77
+ const mapJson = JSON.stringify(map);
78
+ writes.push(fs.promises.writeFile(paths.map, mapJson, "utf8"));
79
+ }
80
+
81
+ // Wait until all files have been written to disk.
82
+ return Promise.all([...writes, ...copies]);
83
+ };
84
+
85
+ function copyFile(file, cwd) {
86
+ return new Promise((resolve, reject) => {
87
+ try {
88
+ const destPath = getDistCopyFilePath({ file, cwd });
89
+ if (!fs.existsSync(dirname(destPath))) {
90
+ fs.mkdirSync(dirname(destPath), { recursive: true });
91
+ }
92
+
93
+ fs.copyFileSync(file, destPath);
94
+ resolve();
95
+ } catch (e) {
96
+ reject(e);
97
+ }
98
+ });
99
+ }
100
+
101
+ function fileEndsWith(file, extensions) {
102
+ return extensions.some(ext => file.endsWith(ext));
103
+ }
@@ -0,0 +1,11 @@
1
+ import fs from "fs";
2
+ import { join } from "path";
3
+
4
+ export const copyToDist = (path, { cwd, logs }) => {
5
+ const from = join(cwd, path);
6
+ const to = join(cwd, "dist", path);
7
+ if (fs.existsSync(from)) {
8
+ fs.copyFileSync(from, to);
9
+ logs !== false && console.log(`Copied ${path}.`);
10
+ }
11
+ };
@@ -0,0 +1,39 @@
1
+ import { dirname, relative } from "path";
2
+ import fg from "fast-glob";
3
+ import fs from "fs";
4
+
5
+ export const replaceTscAliases = async ({ distDir, cwd, debug = false }) => {
6
+ const dtsFiles = await fg("**/*.d.ts", {
7
+ cwd: distDir,
8
+ absolute: true
9
+ });
10
+
11
+ for (const dtsFile of dtsFiles) {
12
+ let content = fs.readFileSync(dtsFile, "utf8");
13
+ let modified = false;
14
+
15
+ // Replace all imports/exports with ~/ alias
16
+ content = content.replace(
17
+ /(from\s+["'])~\/([^"']+)(["'])/g,
18
+ (match, prefix, importPath, suffix) => {
19
+ modified = true;
20
+ // Calculate relative path from current file to dist root
21
+ const fileDir = dirname(dtsFile);
22
+ const relativePath = relative(fileDir, distDir);
23
+ const finalPath = relativePath
24
+ ? `${relativePath}/${importPath}`
25
+ : `./${importPath}`;
26
+ // Normalize to always start with ./
27
+ const normalized = finalPath.startsWith(".") ? finalPath : `./${finalPath}`;
28
+ return `${prefix}${normalized}${suffix}`;
29
+ }
30
+ );
31
+
32
+ if (modified) {
33
+ fs.writeFileSync(dtsFile, content, "utf8");
34
+ if (debug) {
35
+ console.log(`Resolved ~ aliases in: ${relative(cwd, dtsFile)}`);
36
+ }
37
+ }
38
+ }
39
+ };
@@ -0,0 +1,69 @@
1
+ import { join } from "path";
2
+ import ts from "typescript";
3
+ import merge from "lodash/merge.js";
4
+ import { replaceTscAliases } from "./tsAliasReplacer.js";
5
+
6
+ export const tsCompile = async ({ cwd = "", overrides, debug }) => {
7
+ const tsConfigPath = join(cwd, "tsconfig.build.json");
8
+
9
+ let { config: readTsConfig } = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
10
+
11
+ if (overrides.tsConfig) {
12
+ if (typeof overrides.tsConfig === "function") {
13
+ readTsConfig = overrides.tsConfig(readTsConfig);
14
+ } else {
15
+ merge(readTsConfig, overrides.tsConfig);
16
+ }
17
+
18
+ if (debug) {
19
+ console.log(`"tsconfig.build.json" overridden. New config:`);
20
+ console.log(readTsConfig);
21
+ }
22
+ }
23
+ const parsedJsonConfigFile = ts.parseJsonConfigFileContent(readTsConfig, ts.sys, cwd);
24
+
25
+ const { projectReferences, options, fileNames, errors } = parsedJsonConfigFile;
26
+
27
+ // Exclude .d.ts files from TypeScript compilation
28
+ const filteredFileNames = fileNames.filter(fileName => !fileName.endsWith(".d.ts"));
29
+
30
+ const program = ts.createProgram({
31
+ projectReferences,
32
+ options,
33
+ rootNames: filteredFileNames,
34
+ configFileParsingDiagnostics: errors
35
+ });
36
+
37
+ const { diagnostics, emitSkipped } = program.emit(
38
+ undefined, // targetSourceFile
39
+ (fileName, data, writeByteOrderMark, onError, sourceFiles) => {
40
+ // Only emit files within the current package directory
41
+ const relativePath = fileName.replace(cwd, "");
42
+ if (fileName.startsWith(cwd) && !relativePath.includes("../")) {
43
+ ts.sys.writeFile(fileName, data, writeByteOrderMark);
44
+ }
45
+ }
46
+ );
47
+
48
+ const allDiagnostics = ts.getPreEmitDiagnostics(program).concat(diagnostics, errors);
49
+
50
+ if (allDiagnostics.length) {
51
+ const formatHost = {
52
+ getCanonicalFileName: path => path,
53
+ getCurrentDirectory: () => cwd,
54
+ getNewLine: () => ts.sys.newLine
55
+ };
56
+ const message = ts.formatDiagnostics(allDiagnostics, formatHost);
57
+ if (message) {
58
+ throw { message };
59
+ }
60
+ }
61
+
62
+ if (emitSkipped) {
63
+ throw { message: "TypeScript compilation failed." };
64
+ }
65
+
66
+ // Resolve ~ path aliases in .d.ts files
67
+ const distDir = options.outDir || join(cwd, "dist");
68
+ await replaceTscAliases({ distDir, cwd, debug });
69
+ };
@@ -0,0 +1,91 @@
1
+ import fs from "fs";
2
+ import { join } from "path";
3
+ import glob from "fast-glob";
4
+ import { Project, SyntaxKind } from "ts-morph";
5
+ import chalk from "chalk";
6
+
7
+ export const validateEsmImports = async ({ cwd }) => {
8
+ console.log("Validating ESM imports...");
9
+
10
+ const pattern = join(cwd, "src/**/*.{js,ts,tsx}").replace(/\\/g, "/");
11
+ const files = glob.sync(pattern, {
12
+ absolute: true,
13
+ onlyFiles: true
14
+ });
15
+
16
+ const errors = [];
17
+ const project = new Project({
18
+ useInMemoryFileSystem: true,
19
+ skipFileDependencyResolution: true
20
+ });
21
+
22
+ for (const filePath of files) {
23
+ const content = await fs.promises.readFile(filePath, "utf8");
24
+ const sourceFile = project.createSourceFile(filePath, content, { overwrite: true });
25
+
26
+ // Get all import declarations: import { x } from 'y'
27
+ const importDeclarations = sourceFile.getImportDeclarations();
28
+ for (const importDecl of importDeclarations) {
29
+ const spec = importDecl.getModuleSpecifierValue();
30
+ await validateImportSpec(spec, filePath, errors);
31
+ }
32
+
33
+ // Get all export declarations: export { x } from 'y'
34
+ const exportDeclarations = sourceFile.getExportDeclarations();
35
+ for (const exportDecl of exportDeclarations) {
36
+ const moduleSpecifier = exportDecl.getModuleSpecifier();
37
+ if (moduleSpecifier) {
38
+ const spec = moduleSpecifier.getLiteralValue();
39
+ await validateImportSpec(spec, filePath, errors);
40
+ }
41
+ }
42
+
43
+ // Get all dynamic imports: import('y')
44
+ const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
45
+ for (const callExpr of callExpressions) {
46
+ const expression = callExpr.getExpression();
47
+ if (expression.getKind() === SyntaxKind.ImportKeyword) {
48
+ const args = callExpr.getArguments();
49
+ if (args.length > 0 && args[0].getKind() === SyntaxKind.StringLiteral) {
50
+ const spec = args[0].getLiteralValue();
51
+ await validateImportSpec(spec, filePath, errors);
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ if (errors.length > 0) {
58
+ const invalidFiles = [...new Set(errors.map(e => e.file))];
59
+ const errorMessage = [
60
+ chalk.red("ESM import validation failed."),
61
+ `Found ${errors.length} invalid import(s) in ${invalidFiles.length} file(s):`,
62
+ "",
63
+ ...errors.map(e => {
64
+ const relativePath = e.file.replace(cwd, "").replace(/^[\/\\]/, "");
65
+ return ` ${chalk.red("-")} "${chalk.red(e.spec)}" in "${chalk.red(relativePath)}"`;
66
+ })
67
+ ].join("\n");
68
+ throw new Error(errorMessage);
69
+ } else {
70
+ console.log("All ESM imports are valid.");
71
+ }
72
+ };
73
+
74
+ async function validateImportSpec(spec, file, errors) {
75
+ if (spec.startsWith("~") || spec.startsWith("./") || spec.startsWith("../")) {
76
+ // Extract the file extension from the import path
77
+ const lastDotIndex = spec.lastIndexOf(".");
78
+ const lastSlashIndex = Math.max(spec.lastIndexOf("/"), spec.lastIndexOf("\\"));
79
+ const hasExtension = lastDotIndex > lastSlashIndex;
80
+
81
+ // If there's no extension, it should have .js
82
+ if (!hasExtension) {
83
+ const relativePath = file.replace(process.cwd(), "").replace(/^[\/\\]/, "");
84
+ const errorMsg = chalk.red(
85
+ `- Missing .js extension in import "${spec}" in ${relativePath}`
86
+ );
87
+ console.error(errorMsg);
88
+ errors.push({ file, spec, reason: "missing extension" });
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,40 @@
1
+ import * as rimraf from "rimraf";
2
+ import { join } from "path";
3
+ import { babelCompile } from "./buildPackage/babelCompile.js";
4
+ import { tsCompile } from "./buildPackage/tsCompile.js";
5
+ import { copyToDist } from "./buildPackage/copyToDist.js";
6
+ import { validateEsmImports } from "./buildPackage/validateEsmImports.js";
7
+
8
+ export default async options => {
9
+ const start = new Date();
10
+
11
+ if (!options.cwd) {
12
+ options.cwd = "";
13
+ }
14
+ const { cwd = "" } = options;
15
+ options.logs !== false && console.log("Deleting existing build files...");
16
+ rimraf.sync(join(cwd, "./dist"));
17
+ rimraf.sync(join(cwd, "*.tsbuildinfo"), { glob: true });
18
+
19
+ options.logs !== false && console.log("Building...");
20
+
21
+ // Make sure `overrides` is an object.
22
+ if (options.overrides && typeof options.overrides === "string") {
23
+ options.overrides = JSON.parse(options.overrides);
24
+ }
25
+
26
+ // Validate ESM imports before compiling
27
+ await validateEsmImports({ cwd, logs: options.logs });
28
+
29
+ await babelCompile(options);
30
+ await tsCompile(options);
31
+
32
+ options.logs !== false && console.log("Copying meta files...");
33
+ copyToDist("package.json", options);
34
+ copyToDist("LICENSE", options);
35
+
36
+ const duration = (new Date() - start) / 1000;
37
+ options.logs !== false && console.log(`Done! Build finished in ${duration + "s"}.`);
38
+
39
+ return { duration };
40
+ };
@@ -0,0 +1,19 @@
1
+ export default ({ path }) => {
2
+ return {
3
+ presets: [
4
+ ["@babel/preset-react", { useBuiltIns: true }],
5
+ ["@babel/preset-typescript", { isTSX: true, allExtensions: true }]
6
+ ],
7
+ plugins: [
8
+ [
9
+ "babel-plugin-module-resolver",
10
+ {
11
+ cwd: path,
12
+ alias: {
13
+ "~": "./src"
14
+ }
15
+ }
16
+ ]
17
+ ]
18
+ };
19
+ };
@@ -0,0 +1,20 @@
1
+ export default ({ path }) => ({
2
+ presets: [
3
+ ["@babel/preset-react", { useBuiltIns: true }],
4
+ ["@babel/preset-typescript", { isTSX: true, allExtensions: true }]
5
+ ],
6
+ plugins: [
7
+ "babel-plugin-macros",
8
+ "@babel/plugin-proposal-throw-expressions",
9
+ ["@emotion/babel-plugin", { autoLabel: "dev-only" }],
10
+ [
11
+ "babel-plugin-module-resolver",
12
+ {
13
+ cwd: path,
14
+ alias: {
15
+ "~": "./src"
16
+ }
17
+ }
18
+ ]
19
+ ]
20
+ });
@@ -0,0 +1,7 @@
1
+ import buildPackage from "./buildPackage.js";
2
+ import { prepareOptions } from "../utils.js";
3
+
4
+ export default config => async options => {
5
+ const preparedOptions = prepareOptions({ config, options });
6
+ return buildPackage(preparedOptions);
7
+ };
@@ -0,0 +1,45 @@
1
+ import readJsonSync from "read-json-sync";
2
+
3
+ export default ({ path, esm }) => {
4
+ return {
5
+ presets: [
6
+ [
7
+ "@babel/preset-env",
8
+ {
9
+ targets: {
10
+ node: "18"
11
+ },
12
+ modules: esm ? false : "auto",
13
+ exclude: [
14
+ "transform-typeof-symbol",
15
+ "@babel/plugin-proposal-optional-chaining",
16
+ "@babel/plugin-proposal-nullish-coalescing-operator",
17
+ "@babel/plugin-proposal-class-properties",
18
+ "@babel/plugin-transform-async-to-generator",
19
+ "@babel/plugin-transform-regenerator",
20
+ "@babel/plugin-proposal-dynamic-import"
21
+ ]
22
+ }
23
+ ],
24
+ "@babel/preset-typescript"
25
+ ],
26
+ plugins: [
27
+ [
28
+ "@babel/plugin-transform-runtime",
29
+ {
30
+ useESModules: !!esm,
31
+ version: readJsonSync(require.resolve("@babel/runtime/package.json")).version
32
+ }
33
+ ],
34
+ [
35
+ "babel-plugin-module-resolver",
36
+ {
37
+ cwd: path,
38
+ alias: {
39
+ "~": "./src"
40
+ }
41
+ }
42
+ ]
43
+ ]
44
+ };
45
+ };
@@ -0,0 +1,7 @@
1
+ import watchPackage from "./watchPackage.js";
2
+ import { prepareOptions } from "../utils.js";
3
+
4
+ export default config => async options => {
5
+ const preparedOptions = prepareOptions({ config, options });
6
+ return watchPackage(preparedOptions);
7
+ };
@@ -0,0 +1,6 @@
1
+ export { default as watchPackage } from "./watchPackage.js";
2
+ export { default as createWatchPackage } from "./createWatchPackage.js";
3
+ export { default as buildPackage } from "./buildPackage.js";
4
+ export { default as createBuildPackage } from "./createBuildPackage.js";
5
+ export { default as createBabelConfigForNode } from "./createBabelConfigForNode.js";
6
+ export { default as createBabelConfigForReact } from "./createBabelConfigForReact.js";
@@ -0,0 +1,117 @@
1
+ import path from "path";
2
+ import fs from "fs/promises";
3
+ import { transformFileAsync } from "@babel/core";
4
+ import chokidar from "chokidar";
5
+ import baseFs from "node:fs";
6
+
7
+ let compilationQueue = [];
8
+ let debounceTimer = null;
9
+ const DEBOUNCE_DELAY = 1000; // 1 second
10
+
11
+ const flushCompilationQueue = () => {
12
+ if (compilationQueue.length > 0) {
13
+ if (compilationQueue.length === 1) {
14
+ console.log(`Successfully compiled ${compilationQueue[0]}.`);
15
+ } else {
16
+ console.log(`Successfully compiled ${compilationQueue.length} files.`);
17
+ }
18
+ compilationQueue = [];
19
+ }
20
+ };
21
+
22
+ const logCompilation = inputPathRelative => {
23
+ compilationQueue.push(inputPathRelative);
24
+
25
+ clearTimeout(debounceTimer);
26
+ debounceTimer = setTimeout(() => {
27
+ flushCompilationQueue();
28
+ }, DEBOUNCE_DELAY);
29
+ };
30
+
31
+ const getBabelFile = cwd => {
32
+ const babelJs = path.join(cwd, ".babelrc.js");
33
+ const babelCjs = path.join(cwd, ".babelrc.cjs");
34
+
35
+ if (baseFs.existsSync(babelJs)) {
36
+ return babelJs;
37
+ } else if (baseFs.existsSync(babelCjs)) {
38
+ return babelCjs;
39
+ }
40
+ throw new Error("No Babel configuration file found (.babelrc.js or .babelrc.cjs).");
41
+ };
42
+
43
+ const compileFile = async (cwd, inputPath, outputPath) => {
44
+ const inputPathRelative = path.relative(cwd, inputPath);
45
+
46
+ const configFile = getBabelFile(cwd);
47
+
48
+ const result = await transformFileAsync(inputPath, {
49
+ configFile: configFile,
50
+ sourceMaps: true
51
+ });
52
+
53
+ if (!result || !result.code) {
54
+ throw new Error(`Failed to compile: ${inputPath}`);
55
+ }
56
+
57
+ // Write compiled file
58
+ await fs.mkdir(path.dirname(outputPath), { recursive: true });
59
+ await fs.writeFile(outputPath, result.code);
60
+
61
+ // Write source map
62
+ if (result.map) {
63
+ await fs.writeFile(`${outputPath}.map`, JSON.stringify(result.map));
64
+ }
65
+
66
+ logCompilation(inputPathRelative);
67
+ };
68
+
69
+ const srcToDist = filePath =>
70
+ path.join(
71
+ filePath
72
+ .replace(`${path.sep}src${path.sep}`, `${path.sep}dist${path.sep}`)
73
+ .replace(/\.(ts|tsx)$/, ".js")
74
+ );
75
+
76
+ export default async options => {
77
+ const srcDir = path.join(options.cwd, "src");
78
+
79
+ // Watch the src directory recursively for new files
80
+ const watcher = chokidar.watch(srcDir, {
81
+ ignored: /(^|[\/\\])\../, // ignore dotfiles
82
+ persistent: true,
83
+ ignoreInitial: false
84
+ });
85
+
86
+ const isTsFile = filePath => /\.(ts|tsx)$/.test(filePath);
87
+
88
+ watcher.on("add", async srcPath => {
89
+ if (!isTsFile(srcPath)) {
90
+ return;
91
+ }
92
+
93
+ const distPath = srcToDist(srcPath);
94
+
95
+ try {
96
+ await compileFile(options.cwd, srcPath, distPath);
97
+ } catch (err) {
98
+ console.error("Error compiling:", err);
99
+ }
100
+ });
101
+
102
+ watcher.on("change", async srcPath => {
103
+ if (!isTsFile(srcPath)) {
104
+ return;
105
+ }
106
+
107
+ const distPath = srcToDist(srcPath);
108
+
109
+ try {
110
+ await compileFile(options.cwd, srcPath, distPath);
111
+ } catch (err) {
112
+ console.error("Error compiling:", err);
113
+ }
114
+ });
115
+
116
+ console.log("Watching for changes...");
117
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * A utility to recursively traverse loaders and execute the "onLoader" callback.
3
+ */
4
+ export const traverseLoaders = (loaders, onLoader) => {
5
+ for (const loader of loaders) {
6
+ if (loader.oneOf) {
7
+ traverseLoaders(loader.oneOf, onLoader);
8
+ } else if (loader.use) {
9
+ traverseLoaders(loader.use, onLoader);
10
+ } else {
11
+ onLoader(loader);
12
+ }
13
+ }
14
+ };
@@ -0,0 +1,46 @@
1
+ // We'll use this class once the package is converted to TS!
2
+
3
+ import readJson from "read-json-sync";
4
+ import findUp from "find-up";
5
+
6
+ export class PackageJson {
7
+ private readonly filePath: string;
8
+ private readonly json: Record<string, any>;
9
+
10
+ static fromFile(filePath: string) {
11
+ return new PackageJson(filePath, readJson(filePath));
12
+ }
13
+
14
+ static async findClosest(fromPath: string) {
15
+ const closestPackageJson = await findUp("package.json", { cwd: fromPath });
16
+ if (!closestPackageJson) {
17
+ throw Error(`Failed to find ${fromPath}`);
18
+ }
19
+ return PackageJson.fromFile(closestPackageJson);
20
+ }
21
+
22
+ static async fromPackage(packageName: string, cwd?: string) {
23
+ const jsonPath = await findUp(`node_modules/${packageName}/package.json`, {
24
+ cwd: cwd ?? process.cwd()
25
+ });
26
+
27
+ if (!jsonPath) {
28
+ throw Error(`Failed to find package ${packageName}`);
29
+ }
30
+
31
+ return PackageJson.fromFile(jsonPath);
32
+ }
33
+
34
+ private constructor(filePath: string, json: Record<string, any>) {
35
+ this.filePath = filePath;
36
+ this.json = json;
37
+ }
38
+
39
+ getLocation() {
40
+ return this.filePath;
41
+ }
42
+
43
+ getJson() {
44
+ return this.json;
45
+ }
46
+ }
@@ -0,0 +1,33 @@
1
+ export declare class PackageJson {
2
+ private readonly filePath: string;
3
+ private readonly json: Record<string, any>;
4
+
5
+ private constructor(filePath: string, json: Record<string, any>);
6
+
7
+ /**
8
+ * Load a PackageJson instance from a given file path.
9
+ */
10
+ static fromFile(filePath: string): PackageJson;
11
+
12
+ /**
13
+ * Find the closest package.json starting from the given path.
14
+ * Throws if no package.json is found.
15
+ */
16
+ static findClosest(fromPath: string): Promise<PackageJson>;
17
+
18
+ /**
19
+ * Load a PackageJson instance from a package in node_modules.
20
+ * Throws if the package.json cannot be found.
21
+ */
22
+ static fromPackage(packageName: string, cwd?: string): Promise<PackageJson>;
23
+
24
+ /**
25
+ * Get the absolute path to this package.json file.
26
+ */
27
+ getLocation(): string;
28
+
29
+ /**
30
+ * Get the raw JSON contents of the package.json file.
31
+ */
32
+ getJson(): Record<string, any>;
33
+ }