@k8slens/package-build 2.2.3
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.
- package/LICENSE +20 -0
- package/bin/package-build +5 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5781 -0
- package/dist/src/glob-import-plugin.d.ts +3 -0
- package/dist/src/glob-import-plugin.d.ts.map +1 -0
- package/dist/src/package-build.d.ts +2 -0
- package/dist/src/package-build.d.ts.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/index.js +2 -0
- package/package.json +93 -0
- package/src/glob-import-plugin.js +46 -0
- package/src/glob-import-plugin.ts +63 -0
- package/src/glob-import-transformer.js +171 -0
- package/src/image-transformer.js +9 -0
- package/src/package-build.js +193 -0
- package/src/package-build.ts +231 -0
- package/src/svg-transformer.js +7 -0
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@k8slens/package-build",
|
|
3
|
+
"version": "2.2.3",
|
|
4
|
+
"lens": {
|
|
5
|
+
"env": "agnostic",
|
|
6
|
+
"layer": "infrastructure",
|
|
7
|
+
"role": "library",
|
|
8
|
+
"visibility": "public-for-all"
|
|
9
|
+
},
|
|
10
|
+
"private": false,
|
|
11
|
+
"description": "Build script for constituent packages of Lens Desktop",
|
|
12
|
+
"homepage": "https://k8slens.dev",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/lensapp/lens-desktop-monorepo.git"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"author": "Mirantis Inc. <info@k8slens.dev>",
|
|
19
|
+
"type": "commonjs",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"require": "./dist/index.js",
|
|
24
|
+
"import": "./dist/index.js",
|
|
25
|
+
"default": "./dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./transformer": {
|
|
28
|
+
"require": "./src/glob-import-transformer.js",
|
|
29
|
+
"import": "./src/glob-import-transformer.js",
|
|
30
|
+
"default": "./src/glob-import-transformer.js"
|
|
31
|
+
},
|
|
32
|
+
"./svg-transformer": {
|
|
33
|
+
"require": "./src/svg-transformer.js",
|
|
34
|
+
"import": "./src/svg-transformer.js",
|
|
35
|
+
"default": "./src/svg-transformer.js"
|
|
36
|
+
},
|
|
37
|
+
"./png-transformer": {
|
|
38
|
+
"require": "./src/image-transformer.js",
|
|
39
|
+
"import": "./src/image-transformer.js",
|
|
40
|
+
"default": "./src/image-transformer.js"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"bin": {
|
|
44
|
+
"lens-package-build": "bin/package-build"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"index.js",
|
|
48
|
+
"src",
|
|
49
|
+
"bin",
|
|
50
|
+
"dist"
|
|
51
|
+
],
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build:infrastructure:code": "esbuild --bundle index.ts --outfile=dist/index.js --external:node:* --external:postcss --external:esbuild --external:fast-glob --external:esbuild-sass-plugin --external:yargs --external:tailwindcss --format=cjs",
|
|
54
|
+
"build:infrastructure:types": "tsc --project .",
|
|
55
|
+
"build:infrastructure": "npm run build:infrastructure:code; npm run build:infrastructure:types",
|
|
56
|
+
"lint": "lens-verify-code-style",
|
|
57
|
+
"clean": "exit 0",
|
|
58
|
+
"check-types:tests": "lens-types-build --checkTypesTests"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"esbuild": "^0.25.4",
|
|
62
|
+
"esbuild-sass-plugin": "^3.3.1",
|
|
63
|
+
"fast-glob": "^3.3.3",
|
|
64
|
+
"lodash": "^4.17.21",
|
|
65
|
+
"postcss": "^8.5.18",
|
|
66
|
+
"postcss-modules": "^6.0.1",
|
|
67
|
+
"tailwindcss": "^3.4.17",
|
|
68
|
+
"yargs": "^17.7.2"
|
|
69
|
+
},
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"@async-fn/jest": "^1.6.4",
|
|
72
|
+
"@k8slens/typescript": "^7.2.0"
|
|
73
|
+
},
|
|
74
|
+
"publishConfig": {
|
|
75
|
+
"access": "public",
|
|
76
|
+
"registry": "https://registry.npmjs.org/"
|
|
77
|
+
},
|
|
78
|
+
"nx": {
|
|
79
|
+
"targets": {
|
|
80
|
+
"build:infrastructure": {
|
|
81
|
+
"inputs": [
|
|
82
|
+
"{projectRoot}/bin/package-build",
|
|
83
|
+
"{projectRoot}/index.ts",
|
|
84
|
+
"{projectRoot}/src/**/*.ts"
|
|
85
|
+
],
|
|
86
|
+
"outputs": [
|
|
87
|
+
"{projectRoot}/dist/index.js",
|
|
88
|
+
"{projectRoot}/bin/package-build"
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import glob from "fast-glob";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { resolve as resolvePath } from "node:path";
|
|
4
|
+
export const globImportPlugin = () => ({
|
|
5
|
+
name: "glob-import-plugin",
|
|
6
|
+
setup: (build) => {
|
|
7
|
+
const uniquenessCache = new Map();
|
|
8
|
+
build.onResolve({ filter: /\*/ }, async (args) => {
|
|
9
|
+
const { resolveDir, path } = args;
|
|
10
|
+
if (resolveDir === "") {
|
|
11
|
+
return; // Ignore unresolvable paths
|
|
12
|
+
}
|
|
13
|
+
const cacheKey = resolvePath(resolveDir, path);
|
|
14
|
+
const uniqueId = getOrInsertWith(uniquenessCache, cacheKey, getRandomUniqueId);
|
|
15
|
+
return {
|
|
16
|
+
path: `${path}/proof-of-uniqueness/${uniqueId}`,
|
|
17
|
+
namespace: "import-glob",
|
|
18
|
+
pluginData: { resolveDir },
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
build.onLoad({ filter: /.*/, namespace: "import-glob" }, async (args) => {
|
|
22
|
+
const { path, pluginData: { resolveDir }, } = args;
|
|
23
|
+
const [globImportString] = path.split("/proof-of-uniqueness/");
|
|
24
|
+
const files = (await glob(globImportString, { cwd: resolveDir })).sort();
|
|
25
|
+
const contents = [
|
|
26
|
+
files.map((module, index) => `import * as module${index} from './${module}'`).join(";"),
|
|
27
|
+
"",
|
|
28
|
+
`const modules = [${files.map((_, index) => `module${index}`).join(",")}];`,
|
|
29
|
+
"",
|
|
30
|
+
"export default modules;",
|
|
31
|
+
`export const filenames = [${files.map((module) => `'${module}'`).join(",")}]`,
|
|
32
|
+
"",
|
|
33
|
+
].join("\n");
|
|
34
|
+
return { contents, resolveDir };
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
const getRandomUniqueId = () => randomUUID();
|
|
39
|
+
const getOrInsertWith = (map, key, builder) => {
|
|
40
|
+
if (map.has(key)) {
|
|
41
|
+
return map.get(key);
|
|
42
|
+
}
|
|
43
|
+
const value = builder();
|
|
44
|
+
map.set(key, value);
|
|
45
|
+
return value;
|
|
46
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Plugin } from "esbuild";
|
|
2
|
+
import glob from "fast-glob";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { resolve as resolvePath } from "node:path";
|
|
5
|
+
|
|
6
|
+
export const globImportPlugin = (): Plugin => ({
|
|
7
|
+
name: "glob-import-plugin",
|
|
8
|
+
|
|
9
|
+
setup: (build) => {
|
|
10
|
+
const uniquenessCache = new Map<string, string>();
|
|
11
|
+
|
|
12
|
+
build.onResolve({ filter: /\*/ }, async (args) => {
|
|
13
|
+
const { resolveDir, path } = args;
|
|
14
|
+
|
|
15
|
+
if (resolveDir === "") {
|
|
16
|
+
return; // Ignore unresolvable paths
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const cacheKey = resolvePath(resolveDir, path);
|
|
20
|
+
const uniqueId = getOrInsertWith(uniquenessCache, cacheKey, getRandomUniqueId);
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
path: `${path}/proof-of-uniqueness/${uniqueId}`,
|
|
24
|
+
namespace: "import-glob",
|
|
25
|
+
pluginData: { resolveDir },
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
build.onLoad({ filter: /.*/, namespace: "import-glob" }, async (args) => {
|
|
30
|
+
const {
|
|
31
|
+
path,
|
|
32
|
+
pluginData: { resolveDir },
|
|
33
|
+
} = args;
|
|
34
|
+
const [globImportString] = path.split("/proof-of-uniqueness/");
|
|
35
|
+
const files = (await glob(globImportString, { cwd: resolveDir })).sort();
|
|
36
|
+
|
|
37
|
+
const contents = [
|
|
38
|
+
files.map((module, index) => `import * as module${index} from './${module}'`).join(";"),
|
|
39
|
+
"",
|
|
40
|
+
`const modules = [${files.map((_, index) => `module${index}`).join(",")}];`,
|
|
41
|
+
"",
|
|
42
|
+
"export default modules;",
|
|
43
|
+
`export const filenames = [${files.map((module) => `'${module}'`).join(",")}]`,
|
|
44
|
+
"",
|
|
45
|
+
].join("\n");
|
|
46
|
+
|
|
47
|
+
return { contents, resolveDir };
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const getRandomUniqueId = () => randomUUID();
|
|
53
|
+
const getOrInsertWith = <K, V>(map: Map<K, V>, key: K, builder: () => V) => {
|
|
54
|
+
if (map.has(key)) {
|
|
55
|
+
return map.get(key) as V;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const value = builder();
|
|
59
|
+
|
|
60
|
+
map.set(key, value);
|
|
61
|
+
|
|
62
|
+
return value;
|
|
63
|
+
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
const { transformSync } = require("esbuild");
|
|
2
|
+
const { glob, sync: globSync } = require("fast-glob");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const crypto = require("crypto");
|
|
5
|
+
|
|
6
|
+
const loaders = ["js", "jsx", "ts", "tsx"];
|
|
7
|
+
const globImportRegex = /import (?<dynamicImportVariableName>(?:[^,{}\s]+(?:\s*,\s*)?)?(?:\{[^}]*\})?[^,{}\s]*) from "(?<dynamicImportGlob>.*?\*.*?)";/g;
|
|
8
|
+
|
|
9
|
+
const discoverGlobFilesSync = (sourcePath, globText) => globSync(globText, {
|
|
10
|
+
cwd: path.dirname(sourcePath),
|
|
11
|
+
}).sort();
|
|
12
|
+
const discoverGlobFiles = async (sourcePath, globText) => {
|
|
13
|
+
const files = await glob(globText, {
|
|
14
|
+
cwd: path.dirname(sourcePath),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
return files.sort();
|
|
18
|
+
};
|
|
19
|
+
const cacheLength = process.platform === 'win32' ? 16 : 32;
|
|
20
|
+
|
|
21
|
+
// Salts the cache key so that changing this transformer or its esbuild options
|
|
22
|
+
// invalidates entries jest has already written to disk. Bump on every change to
|
|
23
|
+
// generateReplacementCode or getEsbuildTransformOptions.
|
|
24
|
+
const transformerVersion = "2";
|
|
25
|
+
|
|
26
|
+
const generateReplacementCode = (files, dynamicImportVariableName) => {
|
|
27
|
+
// Parse import names inline
|
|
28
|
+
const trimmed = dynamicImportVariableName.trim();
|
|
29
|
+
const defaultImport = trimmed.match(/^([^,{}\s]+)(?:\s*,)?/)?.[1]?.trim();
|
|
30
|
+
const destructuredMatch = trimmed.match(/\{([^}]*)\}/);
|
|
31
|
+
const destructuredImports = destructuredMatch
|
|
32
|
+
? destructuredMatch[1].split(',').map(name => name.trim()).filter(Boolean)
|
|
33
|
+
: [];
|
|
34
|
+
|
|
35
|
+
const imports = files
|
|
36
|
+
.map(
|
|
37
|
+
(module, index) =>
|
|
38
|
+
`import * as ${defaultImport}Module${index} from "./${module}";`,
|
|
39
|
+
)
|
|
40
|
+
.join("\n");
|
|
41
|
+
|
|
42
|
+
const moduleEntries = files.map((file, index) => `${defaultImport}Module${index}`);
|
|
43
|
+
|
|
44
|
+
const declarations = [];
|
|
45
|
+
if (defaultImport) {
|
|
46
|
+
declarations.push(`const ${defaultImport} = [${moduleEntries.join(", ")}];\n`);
|
|
47
|
+
}
|
|
48
|
+
if (destructuredImports.length > 0) {
|
|
49
|
+
const filenamesArray = files.map(file => `"${path.basename(file, path.extname(file))}"`);
|
|
50
|
+
declarations.push(`const { ${destructuredImports.join(', ')} } = { filenames: [${filenamesArray.join(', ')}] };\n`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return `${imports}\n${declarations.join("\n")}`;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Deliberately not salted with NODE_ENV: getEsbuildTransformOptions is a pure function of the
|
|
57
|
+
// file extension, so the output cannot vary with it, and salting only fragmented the cache
|
|
58
|
+
// between local runs (where jest sets NODE_ENV=test) and CI.
|
|
59
|
+
const computeCacheKey = (globImportedFiles, sourceText, sourcePath, options) => {
|
|
60
|
+
return crypto.createHash("sha1")
|
|
61
|
+
.update(transformerVersion)
|
|
62
|
+
.update('\0', 'utf8')
|
|
63
|
+
.update(globImportedFiles.join(","))
|
|
64
|
+
.update('\0', 'utf8')
|
|
65
|
+
.update(sourceText)
|
|
66
|
+
.update('\0', 'utf8')
|
|
67
|
+
.update(options.config.rootDir ? path.relative(options.config.rootDir, sourcePath) : "")
|
|
68
|
+
.update('\0', 'utf8')
|
|
69
|
+
.update(options.instrument ? 'instrument' : '')
|
|
70
|
+
.update('\0', 'utf8')
|
|
71
|
+
.digest('hex')
|
|
72
|
+
.substring(0, cacheLength);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
module.exports = {
|
|
76
|
+
createTransformer: () => ({
|
|
77
|
+
getCacheKey: (sourceText, sourcePath, options) => {
|
|
78
|
+
const matches = sourceText.matchAll(globImportRegex);
|
|
79
|
+
const globImportedFiles = [];
|
|
80
|
+
|
|
81
|
+
for (const match of matches) {
|
|
82
|
+
const { dynamicImportGlob } = match.groups;
|
|
83
|
+
|
|
84
|
+
globImportedFiles.push(...discoverGlobFilesSync(sourcePath, dynamicImportGlob));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return computeCacheKey(globImportedFiles, sourceText, sourcePath, options);
|
|
88
|
+
},
|
|
89
|
+
getCacheKeyAsync: async (sourceText, sourcePath, options) => {
|
|
90
|
+
const matches = sourceText.matchAll(globImportRegex);
|
|
91
|
+
const globImportedFiles = [];
|
|
92
|
+
|
|
93
|
+
for (const match of matches) {
|
|
94
|
+
const { dynamicImportGlob } = match.groups;
|
|
95
|
+
|
|
96
|
+
globImportedFiles.push(...await discoverGlobFiles(sourcePath, dynamicImportGlob));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return computeCacheKey(globImportedFiles, sourceText, sourcePath, options);
|
|
100
|
+
},
|
|
101
|
+
process: (sourceText, sourcePath) => {
|
|
102
|
+
const inputWithNormalImports = replaceGlobImportsWithNormalImportsSync({
|
|
103
|
+
sourceText,
|
|
104
|
+
sourcePath,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return transformSync(inputWithNormalImports, getEsbuildTransformOptions(sourcePath));
|
|
108
|
+
},
|
|
109
|
+
processAsync: async (sourceText, sourcePath) => {
|
|
110
|
+
const inputWithNormalImports = await replaceGlobImportsWithNormalImports({
|
|
111
|
+
sourceText,
|
|
112
|
+
sourcePath,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return transformSync(inputWithNormalImports, getEsbuildTransformOptions(sourcePath));
|
|
116
|
+
},
|
|
117
|
+
}),
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const getEsbuildTransformOptions = (sourcePath) => {
|
|
121
|
+
const extname = path.extname(sourcePath);
|
|
122
|
+
|
|
123
|
+
return ({
|
|
124
|
+
target: "node16",
|
|
125
|
+
platform: "browser",
|
|
126
|
+
format: "cjs",
|
|
127
|
+
loader: loaders.find((x) => `.${x}` === extname),
|
|
128
|
+
// External rather than inline: esbuild then returns the map separately, so
|
|
129
|
+
// jest caches it as its own file and resolves it lazily for stack traces
|
|
130
|
+
// instead of compiling a base64 copy into every suite that loads the module.
|
|
131
|
+
sourcemap: true,
|
|
132
|
+
sourcefile: sourcePath,
|
|
133
|
+
tsconfigRaw: {
|
|
134
|
+
compilerOptions: { experimentalDecorators: true },
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const replaceGlobImportsWithNormalImportsSync = ({ sourceText, sourcePath }) =>
|
|
140
|
+
sourceText.replace(
|
|
141
|
+
globImportRegex,
|
|
142
|
+
(all, dynamicImportVariableName, dynamicImportGlob) => {
|
|
143
|
+
const files = discoverGlobFilesSync(sourcePath, dynamicImportGlob);
|
|
144
|
+
return generateReplacementCode(files, dynamicImportVariableName);
|
|
145
|
+
},
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
async function replaceAsync(text, matcher, replacer) {
|
|
149
|
+
const substrings = [];
|
|
150
|
+
let match;
|
|
151
|
+
let i = 0;
|
|
152
|
+
while ((match = matcher.exec(text)) !== null) {
|
|
153
|
+
substrings.push(text.slice(i, match.index));
|
|
154
|
+
substrings.push(replacer(...match));
|
|
155
|
+
i = matcher.lastIndex;
|
|
156
|
+
}
|
|
157
|
+
// put the rest of text
|
|
158
|
+
substrings.push(text.slice(i));
|
|
159
|
+
// wait for aReplacer calls to finish and join them back into string
|
|
160
|
+
return (await Promise.all(substrings)).join('');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const replaceGlobImportsWithNormalImports = async ({ sourceText, sourcePath }) =>
|
|
164
|
+
replaceAsync(
|
|
165
|
+
sourceText,
|
|
166
|
+
globImportRegex,
|
|
167
|
+
async (all, dynamicImportVariableName, dynamicImportGlob) => {
|
|
168
|
+
const files = await discoverGlobFiles(sourcePath, dynamicImportGlob);
|
|
169
|
+
return generateReplacementCode(files, dynamicImportVariableName);
|
|
170
|
+
},
|
|
171
|
+
);
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { build } from "esbuild";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { sassPlugin, postcssModules } from "esbuild-sass-plugin";
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { builtinModules as rawBuiltinModules } from "node:module";
|
|
6
|
+
import yargs from "yargs";
|
|
7
|
+
import { globImportPlugin } from "./glob-import-plugin";
|
|
8
|
+
import tailwindcss from "tailwindcss";
|
|
9
|
+
import { partition } from "lodash";
|
|
10
|
+
const CWD = process.cwd();
|
|
11
|
+
const packageJson = JSON.parse(readFileSync(path.resolve(CWD, "package.json"), "utf-8"));
|
|
12
|
+
const isExperimentalModule = (module) => module.startsWith("_");
|
|
13
|
+
const alsoAddNodeRefs = (module) => [module, `node:${module}`];
|
|
14
|
+
const { builtinModules, disallowedBuiltinModules } = (() => {
|
|
15
|
+
const [disallowed, allowed] = partition(rawBuiltinModules, isExperimentalModule);
|
|
16
|
+
const disallowedBuiltinModules = new Set(disallowed.flatMap(alsoAddNodeRefs));
|
|
17
|
+
const builtinModules = new Set(allowed.flatMap(alsoAddNodeRefs));
|
|
18
|
+
return { disallowedBuiltinModules, builtinModules };
|
|
19
|
+
})();
|
|
20
|
+
const getPackageName = (path) => {
|
|
21
|
+
const [firstPart, secondPart] = path.split("/");
|
|
22
|
+
if (path.startsWith("@")) {
|
|
23
|
+
return [firstPart, secondPart].join("/");
|
|
24
|
+
}
|
|
25
|
+
return firstPart;
|
|
26
|
+
};
|
|
27
|
+
const bundleDependency = null;
|
|
28
|
+
const ensureCorrectImportsPlugin = {
|
|
29
|
+
name: "ensure-correct-imports",
|
|
30
|
+
setup: (build) => {
|
|
31
|
+
build.onResolve({ filter: /^\./ }, (args) => {
|
|
32
|
+
const fullPath = path.resolve(args.resolveDir, args.path);
|
|
33
|
+
const relativePathToImporter = path.relative(CWD, args.importer);
|
|
34
|
+
const relativePath = path.relative(CWD, fullPath);
|
|
35
|
+
if (relativePath.startsWith("..") && !relativePathToImporter.startsWith("..")) {
|
|
36
|
+
throw new Error(`Tried to import "${args.path}" from "${relativePathToImporter}" but it is outside of the package.`);
|
|
37
|
+
}
|
|
38
|
+
return bundleDependency;
|
|
39
|
+
});
|
|
40
|
+
build.onResolve({ filter: /^[^.]/ }, async (args) => {
|
|
41
|
+
// Enforce declared imports only for the package's own sources (.ts/.tsx/.js/.jsx).
|
|
42
|
+
// Anything imported from outside the package directory — node_modules files or
|
|
43
|
+
// other workspace packages' dists (whose realpaths live outside node_modules) —
|
|
44
|
+
// belongs to an already-bundled dependency and must keep bundling freely.
|
|
45
|
+
const relativePathToImporter = path.relative(CWD, args.importer);
|
|
46
|
+
const importerIsOwnFile = !relativePathToImporter.startsWith("..") &&
|
|
47
|
+
!relativePathToImporter.split(path.sep).includes("node_modules");
|
|
48
|
+
const importerIsSourceFile = /\.(t|j)sx?$/.test(args.importer);
|
|
49
|
+
if (!importerIsOwnFile || !importerIsSourceFile) {
|
|
50
|
+
return bundleDependency;
|
|
51
|
+
}
|
|
52
|
+
if (disallowedBuiltinModules.has(args.path)) {
|
|
53
|
+
throw new Error(`Tried to import "${args.path}" from "${args.importer}" but it is a disallowed builtin module.`);
|
|
54
|
+
}
|
|
55
|
+
if (builtinModules.has(args.path)) {
|
|
56
|
+
return {
|
|
57
|
+
external: true,
|
|
58
|
+
path: args.path,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const packageName = getPackageName(args.path);
|
|
62
|
+
const isExplicitDependency = packageName in (packageJson.dependencies ?? {});
|
|
63
|
+
const isExplicitDevDependency = packageName in (packageJson.devDependencies ?? {});
|
|
64
|
+
if (isExplicitDependency) {
|
|
65
|
+
// Note: Return the full path since we assume that it is a "exports" entry of the dependency found in package.json
|
|
66
|
+
return {
|
|
67
|
+
external: true,
|
|
68
|
+
path: args.path,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (isExplicitDevDependency) {
|
|
72
|
+
return bundleDependency;
|
|
73
|
+
}
|
|
74
|
+
throw new Error(`Tried to import "${args.path}" from "${relativePathToImporter}" but it is not a dependency of the package.`);
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
const parsedArgs = yargs()
|
|
79
|
+
.options({
|
|
80
|
+
tsconfig: {
|
|
81
|
+
type: "string",
|
|
82
|
+
default: "tsconfig.json",
|
|
83
|
+
description: 'Custom tsconfig.json -file, defaults to "tsconfig.json"',
|
|
84
|
+
},
|
|
85
|
+
"entry-point": {
|
|
86
|
+
type: "string",
|
|
87
|
+
default: "index.ts",
|
|
88
|
+
description: 'Custom entry-point, defaults to "index.ts"',
|
|
89
|
+
},
|
|
90
|
+
outfile: {
|
|
91
|
+
type: "string",
|
|
92
|
+
default: path.resolve(CWD, "dist/index.js"),
|
|
93
|
+
description: `Custom outfile, defaults to "dist/index.js"`,
|
|
94
|
+
},
|
|
95
|
+
format: {
|
|
96
|
+
choices: ["cjs", "esm"],
|
|
97
|
+
default: "cjs",
|
|
98
|
+
description: `Transpilation output format, defaults to "cjs"`,
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
.parseSync(process.argv);
|
|
102
|
+
// Copied from https://github.com/evanw/esbuild/issues/1051
|
|
103
|
+
const nativeNodeModulesPlugin = {
|
|
104
|
+
name: "native-node-modules",
|
|
105
|
+
setup(build) {
|
|
106
|
+
// If a ".node" file is imported within a module in the "file" namespace, resolve
|
|
107
|
+
// it to an absolute path and put it into the "node-file" virtual namespace.
|
|
108
|
+
build.onResolve({ filter: /\.node$/, namespace: "file" }, (args) => ({
|
|
109
|
+
path: require.resolve(args.path, { paths: [args.resolveDir] }),
|
|
110
|
+
namespace: "node-file",
|
|
111
|
+
}));
|
|
112
|
+
// Files in the "node-file" virtual namespace call "require()" on the
|
|
113
|
+
// path from esbuild of the ".node" file in the output directory.
|
|
114
|
+
build.onLoad({ filter: /.*/, namespace: "node-file" }, (args) => ({
|
|
115
|
+
contents: `
|
|
116
|
+
import path from ${JSON.stringify(args.path)}
|
|
117
|
+
try { module.exports = require(path) }
|
|
118
|
+
catch {}
|
|
119
|
+
`,
|
|
120
|
+
}));
|
|
121
|
+
// If a ".node" file is imported within a module in the "node-file" namespace, put
|
|
122
|
+
// it in the "file" namespace where esbuild's default loading behavior will handle
|
|
123
|
+
// it. It is already an absolute path since we resolved it to one above.
|
|
124
|
+
build.onResolve({ filter: /\.node$/, namespace: "node-file" }, (args) => ({
|
|
125
|
+
path: args.path,
|
|
126
|
+
namespace: "file",
|
|
127
|
+
}));
|
|
128
|
+
// Tell esbuild's default loading behavior to use the "file" loader for
|
|
129
|
+
// these ".node" files.
|
|
130
|
+
const opts = build.initialOptions;
|
|
131
|
+
opts.loader = opts.loader || {};
|
|
132
|
+
opts.loader[".node"] = "file";
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
// Keep .png imports as require() calls in the dist bundle instead of inlining
|
|
136
|
+
// them as base64 data urls, so images ship as files loaded on demand rather
|
|
137
|
+
// than megabytes of string literals in every consuming bundle. The import
|
|
138
|
+
// path is rewritten to stay correct relative to the output file, since the
|
|
139
|
+
// application build resolves it from there with its "file" loader.
|
|
140
|
+
const externalImagesPlugin = {
|
|
141
|
+
name: "external-images",
|
|
142
|
+
setup(build) {
|
|
143
|
+
build.onResolve({ filter: /\.png$/ }, (args) => {
|
|
144
|
+
const absolutePath = path.resolve(args.resolveDir, args.path);
|
|
145
|
+
const outputDirectory = path.dirname(path.resolve(CWD, parsedArgs.outfile));
|
|
146
|
+
const relativePath = path.relative(outputDirectory, absolutePath).split(path.sep).join("/");
|
|
147
|
+
return {
|
|
148
|
+
path: relativePath.startsWith(".") ? relativePath : `./${relativePath}`,
|
|
149
|
+
external: true,
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
export const packageBuild = async () => {
|
|
155
|
+
try {
|
|
156
|
+
await build({
|
|
157
|
+
entryPoints: [parsedArgs.entryPoint],
|
|
158
|
+
tsconfig: parsedArgs.tsconfig,
|
|
159
|
+
outfile: parsedArgs.outfile,
|
|
160
|
+
format: parsedArgs.format,
|
|
161
|
+
bundle: true,
|
|
162
|
+
platform: "node",
|
|
163
|
+
target: "node16",
|
|
164
|
+
sourcemap: "inline",
|
|
165
|
+
plugins: [
|
|
166
|
+
ensureCorrectImportsPlugin,
|
|
167
|
+
externalImagesPlugin,
|
|
168
|
+
globImportPlugin(),
|
|
169
|
+
sassPlugin({
|
|
170
|
+
filter: /\.module\.scss$/,
|
|
171
|
+
transform: postcssModules({
|
|
172
|
+
basedir: __dirname,
|
|
173
|
+
generateScopedName: "[name]__[local]___[hash:base64:5]",
|
|
174
|
+
}, [tailwindcss]),
|
|
175
|
+
}),
|
|
176
|
+
sassPlugin({
|
|
177
|
+
filter: /\.scss$/,
|
|
178
|
+
}),
|
|
179
|
+
nativeNodeModulesPlugin,
|
|
180
|
+
],
|
|
181
|
+
loader: {
|
|
182
|
+
".ttf": "dataurl",
|
|
183
|
+
".svg": "text",
|
|
184
|
+
".yaml": "text",
|
|
185
|
+
".yml": "text",
|
|
186
|
+
".yml.hb": "text",
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
};
|