@checkdigit/typescript-config 7.0.0-PR.54-a1c4 → 7.0.0-PR.54-2621

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.
@@ -0,0 +1,30 @@
1
+ // src/analyze.ts
2
+ import { strict as assert } from "node:assert";
3
+ function analyze(metafile) {
4
+ const source = new Set(Object.keys(metafile.inputs).filter((key) => !key.startsWith("node_modules")));
5
+ const modules = new Set(Object.keys(metafile.inputs).filter((key) => key.startsWith("node_modules")));
6
+ const [output] = Object.entries(metafile.outputs);
7
+ assert.ok(output !== void 0);
8
+ const [, bundle] = output;
9
+ const sourceBytes = Object.entries(bundle.inputs).reduce((bytes, [file, value]) => {
10
+ if (source.has(file)) {
11
+ return bytes + value.bytesInOutput;
12
+ }
13
+ return bytes;
14
+ }, 0);
15
+ const moduleBytes = Object.entries(bundle.inputs).reduce((bytes, [file, value]) => {
16
+ if (modules.has(file)) {
17
+ return bytes + value.bytesInOutput;
18
+ }
19
+ return bytes;
20
+ }, 0);
21
+ return {
22
+ sourceBytes,
23
+ moduleBytes,
24
+ totalBytes: bundle.bytes
25
+ };
26
+ }
27
+ export {
28
+ analyze as default
29
+ };
30
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL2FuYWx5emUudHMiXSwKICAibWFwcGluZ3MiOiAiO0FBRUEsU0FBUyxVQUFVLGNBQWM7QUFJbEIsU0FBUixRQUF5QixVQUFvQjtBQUNsRCxRQUFNLFNBQVMsSUFBSSxJQUFJLE9BQU8sS0FBSyxTQUFTLE1BQU0sRUFBRSxPQUFPLENBQUMsUUFBUSxDQUFDLElBQUksV0FBVyxjQUFjLENBQUMsQ0FBQztBQUNwRyxRQUFNLFVBQVUsSUFBSSxJQUFJLE9BQU8sS0FBSyxTQUFTLE1BQU0sRUFBRSxPQUFPLENBQUMsUUFBUSxJQUFJLFdBQVcsY0FBYyxDQUFDLENBQUM7QUFFcEcsUUFBTSxDQUFDLE1BQU0sSUFBSSxPQUFPLFFBQVEsU0FBUyxPQUFPO0FBQ2hELFNBQU8sR0FBRyxXQUFXLE1BQVM7QUFDOUIsUUFBTSxDQUFDLEVBQUUsTUFBTSxJQUFJO0FBRW5CLFFBQU0sY0FBYyxPQUFPLFFBQVEsT0FBTyxNQUFNLEVBQUUsT0FBTyxDQUFDLE9BQU8sQ0FBQyxNQUFNLEtBQUssTUFBTTtBQUNqRixRQUFJLE9BQU8sSUFBSSxJQUFJLEdBQUc7QUFDcEIsYUFBTyxRQUFRLE1BQU07QUFBQSxJQUN2QjtBQUNBLFdBQU87QUFBQSxFQUNULEdBQUcsQ0FBQztBQUVKLFFBQU0sY0FBYyxPQUFPLFFBQVEsT0FBTyxNQUFNLEVBQUUsT0FBTyxDQUFDLE9BQU8sQ0FBQyxNQUFNLEtBQUssTUFBTTtBQUNqRixRQUFJLFFBQVEsSUFBSSxJQUFJLEdBQUc7QUFDckIsYUFBTyxRQUFRLE1BQU07QUFBQSxJQUN2QjtBQUNBLFdBQU87QUFBQSxFQUNULEdBQUcsQ0FBQztBQUVKLFNBQU87QUFBQSxJQUNMO0FBQUEsSUFDQTtBQUFBLElBQ0EsWUFBWSxPQUFPO0FBQUEsRUFDckI7QUFDRjsiLAogICJuYW1lcyI6IFtdCn0K
@@ -0,0 +1,47 @@
1
+ // src/builder.ts
2
+ import { strict as assert } from "node:assert";
3
+ import { promises as fs } from "node:fs";
4
+ import path from "node:path";
5
+ import { parseArgs } from "node:util";
6
+ import { compile, analyze } from "./index.mjs";
7
+ var {
8
+ values: { type, inDir, outDir, entryPoint, outFile, external, minify, sourceMap }
9
+ } = parseArgs({
10
+ options: {
11
+ type: { type: "string", short: "t", default: "module" },
12
+ inDir: { type: "string", short: "i", default: "src" },
13
+ outDir: { type: "string", short: "o", default: "build" },
14
+ entryPoint: { type: "string", short: "e", default: void 0 },
15
+ outFile: { type: "string", short: "f", default: void 0 },
16
+ external: { type: "string", short: "x", multiple: true, default: [] },
17
+ minify: { type: "boolean", short: "m", default: false },
18
+ sourceMap: { type: "boolean", short: "s", default: false }
19
+ }
20
+ });
21
+ assert.ok(type === "module" || type === "types", "type must be types or module");
22
+ assert.ok(inDir !== void 0, "inDir is required");
23
+ assert.ok(outDir !== void 0, "outDir is required");
24
+ var compileResult = await compile({
25
+ type,
26
+ inDir: path.join(process.cwd(), inDir),
27
+ outDir: path.join(process.cwd(), outDir),
28
+ entryPoint,
29
+ outFile,
30
+ external,
31
+ minify,
32
+ sourceMap
33
+ });
34
+ await Promise.all(
35
+ compileResult.outputFiles.map(async (file) => {
36
+ await fs.mkdir(path.join(path.dirname(file.path)), { recursive: true });
37
+ await fs.writeFile(file.path, file.text);
38
+ })
39
+ );
40
+ if (compileResult.metafile !== void 0) {
41
+ const analysis = analyze(compileResult.metafile);
42
+ await fs.writeFile(path.join(outDir, "metafile.json"), JSON.stringify(compileResult.metafile, void 0, 2));
43
+ console.log(
44
+ `${outFile}: src ${analysis.sourceBytes}, node_modules ${analysis.moduleBytes}, total ${analysis.totalBytes}`
45
+ );
46
+ }
47
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL2J1aWxkZXIudHMiXSwKICAibWFwcGluZ3MiOiAiO0FBRUEsU0FBUyxVQUFVLGNBQWM7QUFDakMsU0FBUyxZQUFZLFVBQVU7QUFDL0IsT0FBTyxVQUFVO0FBQ2pCLFNBQVMsaUJBQWlCO0FBRTFCLFNBQVMsU0FBUyxlQUFlO0FBRWpDLElBQU07QUFBQSxFQUNKLFFBQVEsRUFBRSxNQUFNLE9BQU8sUUFBUSxZQUFZLFNBQVMsVUFBVSxRQUFRLFVBQVU7QUFDbEYsSUFBSSxVQUFVO0FBQUEsRUFDWixTQUFTO0FBQUEsSUFDUCxNQUFNLEVBQUUsTUFBTSxVQUFVLE9BQU8sS0FBSyxTQUFTLFNBQVM7QUFBQSxJQUN0RCxPQUFPLEVBQUUsTUFBTSxVQUFVLE9BQU8sS0FBSyxTQUFTLE1BQU07QUFBQSxJQUNwRCxRQUFRLEVBQUUsTUFBTSxVQUFVLE9BQU8sS0FBSyxTQUFTLFFBQVE7QUFBQSxJQUN2RCxZQUFZLEVBQUUsTUFBTSxVQUFVLE9BQU8sS0FBSyxTQUFTLE9BQVU7QUFBQSxJQUM3RCxTQUFTLEVBQUUsTUFBTSxVQUFVLE9BQU8sS0FBSyxTQUFTLE9BQVU7QUFBQSxJQUMxRCxVQUFVLEVBQUUsTUFBTSxVQUFVLE9BQU8sS0FBSyxVQUFVLE1BQU0sU0FBUyxDQUFDLEVBQUU7QUFBQSxJQUNwRSxRQUFRLEVBQUUsTUFBTSxXQUFXLE9BQU8sS0FBSyxTQUFTLE1BQU07QUFBQSxJQUN0RCxXQUFXLEVBQUUsTUFBTSxXQUFXLE9BQU8sS0FBSyxTQUFTLE1BQU07QUFBQSxFQUMzRDtBQUNGLENBQUM7QUFFRCxPQUFPLEdBQUcsU0FBUyxZQUFZLFNBQVMsU0FBUyw4QkFBOEI7QUFDL0UsT0FBTyxHQUFHLFVBQVUsUUFBVyxtQkFBbUI7QUFDbEQsT0FBTyxHQUFHLFdBQVcsUUFBVyxvQkFBb0I7QUFFcEQsSUFBTSxnQkFBZ0IsTUFBTSxRQUFRO0FBQUEsRUFDbEM7QUFBQSxFQUNBLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxHQUFHLEtBQUs7QUFBQSxFQUNyQyxRQUFRLEtBQUssS0FBSyxRQUFRLElBQUksR0FBRyxNQUFNO0FBQUEsRUFDdkM7QUFBQSxFQUNBO0FBQUEsRUFDQTtBQUFBLEVBQ0E7QUFBQSxFQUNBO0FBQ0YsQ0FBQztBQUdELE1BQU0sUUFBUTtBQUFBLEVBQ1osY0FBYyxZQUFZLElBQUksT0FBTyxTQUFTO0FBQzVDLFVBQU0sR0FBRyxNQUFNLEtBQUssS0FBSyxLQUFLLFFBQVEsS0FBSyxJQUFJLENBQUMsR0FBRyxFQUFFLFdBQVcsS0FBSyxDQUFDO0FBQ3RFLFVBQU0sR0FBRyxVQUFVLEtBQUssTUFBTSxLQUFLLElBQUk7QUFBQSxFQUN6QyxDQUFDO0FBQ0g7QUFHQSxJQUFJLGNBQWMsYUFBYSxRQUFXO0FBQ3hDLFFBQU0sV0FBVyxRQUFRLGNBQWMsUUFBUTtBQUMvQyxRQUFNLEdBQUcsVUFBVSxLQUFLLEtBQUssUUFBUSxlQUFlLEdBQUcsS0FBSyxVQUFVLGNBQWMsVUFBVSxRQUFXLENBQUMsQ0FBQztBQUczRyxVQUFRO0FBQUEsSUFDTixHQUFHLE9BQU8sU0FBUyxTQUFTLFdBQVcsa0JBQWtCLFNBQVMsV0FBVyxXQUFXLFNBQVMsVUFBVTtBQUFBLEVBQzdHO0FBQ0Y7IiwKICAibmFtZXMiOiBbXQp9Cg==
@@ -0,0 +1,182 @@
1
+ // src/compile.ts
2
+ import { strict as assert } from "node:assert";
3
+ import { promises as fs } from "node:fs";
4
+ import path from "node:path";
5
+ import typescript from "typescript";
6
+ import { build } from "esbuild";
7
+ var commonJsCompatabilityBanner = `import { createRequire as __createRequire } from "node:module";
8
+ import { fileURLToPath as __fileURLToPath } from "node:url";
9
+ import { default as __path } from "node:path";
10
+ const __filename = __fileURLToPath(import.meta.url);
11
+ const __dirname = __path.dirname(__filename);
12
+ const require = __createRequire(import.meta.url);`;
13
+ async function getFiles(directory) {
14
+ const entries = await fs.readdir(directory, { withFileTypes: true });
15
+ const files = await Promise.all(
16
+ entries.map((entry) => {
17
+ const result = path.resolve(directory, entry.name);
18
+ return entry.isDirectory() ? getFiles(result) : result;
19
+ })
20
+ );
21
+ return files.flat();
22
+ }
23
+ function excludeSourceMaps(filter) {
24
+ return (pluginBuild) => {
25
+ pluginBuild.onLoad({ filter }, async (args) => {
26
+ if (args.path.endsWith(".js") || args.path.endsWith(".mjs")) {
27
+ return {
28
+ contents: `${await fs.readFile(
29
+ args.path,
30
+ "utf8"
31
+ )}
32
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIiJdLCJtYXBwaW5ncyI6IkEifQ==`,
33
+ loader: "default"
34
+ };
35
+ }
36
+ return void 0;
37
+ });
38
+ };
39
+ }
40
+ function resolveTypescriptPaths() {
41
+ return (pluginBuild) => {
42
+ pluginBuild.onResolve({ filter: /.*/u }, async (resolved) => {
43
+ if (resolved.kind === "entry-point" || !resolved.path.startsWith(".") || resolved.path.endsWith(".js") || resolved.path.endsWith(".json")) {
44
+ return { external: resolved.kind !== "entry-point" };
45
+ }
46
+ let isDirectory = false;
47
+ try {
48
+ const stats = await fs.lstat(path.join(resolved.resolveDir, resolved.path));
49
+ isDirectory = stats.isDirectory();
50
+ } catch {
51
+ }
52
+ let newPath = resolved.path;
53
+ newPath += isDirectory ? `/index.mjs` : `.mjs`;
54
+ return { path: newPath, external: true };
55
+ });
56
+ };
57
+ }
58
+ async function compile_default({
59
+ type,
60
+ entryPoint,
61
+ inDir,
62
+ outDir,
63
+ outFile,
64
+ external = [],
65
+ minify = false,
66
+ sourceMap,
67
+ workingDirectory = process.cwd()
68
+ }) {
69
+ const messages = [];
70
+ assert.ok(
71
+ entryPoint === void 0 && outFile === void 0 || entryPoint !== void 0 && outFile !== void 0,
72
+ "entryPoint and outFile must both be provided"
73
+ );
74
+ const allSourceFiles = await getFiles(inDir);
75
+ const productionSourceFiles = entryPoint === void 0 ? allSourceFiles.filter((file) => file.endsWith(".ts")) : [path.join(inDir, entryPoint)];
76
+ const program = typescript.createProgram(productionSourceFiles, {
77
+ module: typescript.ModuleKind.ESNext,
78
+ moduleResolution: typescript.ModuleResolutionKind.Bundler,
79
+ target: typescript.ScriptTarget.ESNext,
80
+ declaration: true,
81
+ noEmit: type !== "types",
82
+ noEmitOnError: true,
83
+ emitDeclarationOnly: type === "types",
84
+ rootDir: inDir,
85
+ outDir,
86
+ noLib: false,
87
+ skipLibCheck: true,
88
+ strict: true,
89
+ preserveConstEnums: true,
90
+ noImplicitReturns: true,
91
+ noUnusedLocals: true,
92
+ noUnusedParameters: true,
93
+ alwaysStrict: true,
94
+ verbatimModuleSyntax: true,
95
+ forceConsistentCasingInFileNames: true,
96
+ emitDecoratorMetadata: true,
97
+ experimentalDecorators: true,
98
+ resolveJsonModule: true,
99
+ esModuleInterop: true,
100
+ noUncheckedIndexedAccess: true,
101
+ noPropertyAccessFromIndexSignature: true,
102
+ allowUnusedLabels: false,
103
+ allowUnreachableCode: false,
104
+ noImplicitOverride: true,
105
+ useUnknownInCatchVariables: true,
106
+ exactOptionalPropertyTypes: true
107
+ });
108
+ const declarationFiles = [];
109
+ const emitResult = program.emit(void 0, (fileName, data) => {
110
+ declarationFiles.push({ path: fileName, text: data });
111
+ });
112
+ const allDiagnostics = typescript.sortAndDeduplicateDiagnostics([
113
+ ...typescript.getPreEmitDiagnostics(program),
114
+ ...emitResult.diagnostics
115
+ ]);
116
+ for (const diagnostic of allDiagnostics) {
117
+ if (diagnostic.file) {
118
+ assert.ok(diagnostic.start !== void 0);
119
+ const { line, character } = typescript.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
120
+ const message = typescript.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
121
+ messages.push(`tsc: ${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
122
+ } else {
123
+ messages.push(`tsc: ${typescript.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}`);
124
+ }
125
+ }
126
+ if (messages.length > 0) {
127
+ throw new Error(`tsc failed ${JSON.stringify(messages)}`);
128
+ }
129
+ if (type === "types") {
130
+ return {
131
+ outputFiles: declarationFiles
132
+ };
133
+ }
134
+ const buildResult = await build({
135
+ entryPoints: productionSourceFiles,
136
+ bundle: true,
137
+ minify,
138
+ absWorkingDir: workingDirectory,
139
+ platform: "node",
140
+ format: "esm",
141
+ treeShaking: type === "module",
142
+ write: false,
143
+ metafile: outFile !== void 0,
144
+ sourcesContent: false,
145
+ banner: type === "module" && outFile !== void 0 ? {
146
+ js: commonJsCompatabilityBanner
147
+ } : {},
148
+ sourcemap: sourceMap ? "inline" : false,
149
+ ...outFile === void 0 ? {
150
+ // individual files
151
+ outdir: outDir,
152
+ outExtension: { ".js": ".mjs" },
153
+ plugins: [
154
+ {
155
+ name: "resolve-typescript-paths",
156
+ setup: resolveTypescriptPaths()
157
+ }
158
+ ]
159
+ } : {
160
+ // bundling
161
+ outfile: path.join(outDir, outFile),
162
+ legalComments: "none",
163
+ external,
164
+ plugins: [
165
+ {
166
+ name: "exclude-source-maps",
167
+ setup: excludeSourceMaps(/node_modules/u)
168
+ }
169
+ ]
170
+ }
171
+ });
172
+ messages.push(...buildResult.errors.map((error) => `esbuild error: ${error.text}`));
173
+ messages.push(...buildResult.warnings.map((warning) => `esbuild warning: ${warning.text}`));
174
+ if (messages.length > 0) {
175
+ throw new Error(`esbuild failed ${JSON.stringify(messages)}`);
176
+ }
177
+ return buildResult;
178
+ }
179
+ export {
180
+ compile_default as default
181
+ };
182
+ //# sourceMappingURL=data:application/json;base64,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
@@ -0,0 +1,10 @@
1
+ // src/index.ts
2
+ export * from "./analyze.mjs";
3
+ export * from "./compile.mjs";
4
+ import { default as default2 } from "./analyze.mjs";
5
+ import { default as default3 } from "./compile.mjs";
6
+ export {
7
+ default2 as analyze,
8
+ default3 as compile
9
+ };
10
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL2luZGV4LnRzIl0sCiAgIm1hcHBpbmdzIjogIjtBQUVBLGNBQWM7QUFDZCxjQUFjO0FBRWQsU0FBb0IsV0FBWEEsZ0JBQTBCO0FBQ25DLFNBQW9CLFdBQVhBLGdCQUEwQjsiLAogICJuYW1lcyI6IFsiZGVmYXVsdCJdCn0K
@@ -0,0 +1,6 @@
1
+ // src/test/module-directory/index.ts
2
+ var module_directory_default = () => "module-directory-index";
3
+ export {
4
+ module_directory_default as default
5
+ };
6
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL3Rlc3QvbW9kdWxlLWRpcmVjdG9yeS9pbmRleC50cyJdLAogICJtYXBwaW5ncyI6ICI7QUFFQSxJQUFPLDJCQUFRLE1BQU07IiwKICAibmFtZXMiOiBbXQp9Cg==
@@ -0,0 +1,6 @@
1
+ import type { Metafile } from 'esbuild';
2
+ export default function analyze(metafile: Metafile): {
3
+ sourceBytes: number;
4
+ moduleBytes: number;
5
+ totalBytes: number;
6
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,81 @@
1
+ export type ImportKind = 'entry-point' | 'import-statement' | 'require-call' | 'dynamic-import' | 'require-resolve' | 'import-rule' | 'composes-from' | 'url-token';
2
+ export interface Metafile {
3
+ inputs: {
4
+ [path: string]: {
5
+ bytes: number;
6
+ imports: {
7
+ path: string;
8
+ kind: ImportKind;
9
+ external?: boolean;
10
+ original?: string;
11
+ }[];
12
+ format?: 'cjs' | 'esm';
13
+ };
14
+ };
15
+ outputs: {
16
+ [path: string]: {
17
+ bytes: number;
18
+ inputs: {
19
+ [path: string]: {
20
+ bytesInOutput: number;
21
+ };
22
+ };
23
+ imports: {
24
+ path: string;
25
+ kind: ImportKind | 'file-loader';
26
+ external?: boolean;
27
+ }[];
28
+ exports: string[];
29
+ entryPoint?: string;
30
+ cssBundle?: string;
31
+ };
32
+ };
33
+ }
34
+ export interface OutputFile {
35
+ path: string;
36
+ text: string;
37
+ }
38
+ export interface CompileResult {
39
+ metafile?: Metafile | undefined;
40
+ outputFiles: OutputFile[];
41
+ }
42
+ export interface CompileOptions {
43
+ /**
44
+ * whether to produce Typescript types or ESM code
45
+ */
46
+ type: 'module' | 'types';
47
+ /**
48
+ * the entry point for the bundle, relative to the inDir. if not provided, the files in the inDir will be processed
49
+ * as individual unbundled files
50
+ */
51
+ entryPoint?: string | undefined;
52
+ /**
53
+ * source code
54
+ */
55
+ inDir: string;
56
+ /**
57
+ * build directory
58
+ */
59
+ outDir: string;
60
+ /**
61
+ * build file, relative to the outDir
62
+ */
63
+ outFile?: string | undefined;
64
+ /**
65
+ * external modules to exclude from the bundle
66
+ */
67
+ external?: string[] | undefined;
68
+ /**
69
+ * whether to minify output
70
+ */
71
+ minify?: boolean | undefined;
72
+ /**
73
+ * whether to include sourcemap
74
+ */
75
+ sourceMap?: boolean | undefined;
76
+ /**
77
+ * working directory
78
+ */
79
+ workingDirectory?: string | undefined;
80
+ }
81
+ export default function ({ type, entryPoint, inDir, outDir, outFile, external, minify, sourceMap, workingDirectory, }: CompileOptions): Promise<CompileResult>;
@@ -0,0 +1,4 @@
1
+ export * from './analyze';
2
+ export * from './compile';
3
+ export { default as analyze } from './analyze';
4
+ export { default as compile } from './compile';
@@ -0,0 +1,2 @@
1
+ declare const _default: () => string;
2
+ export default _default;
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@checkdigit/typescript-config","version":"7.0.0-PR.54-a1c4","description":"Check Digit standard Typescript configuration","prettier":"@checkdigit/prettier-config","engines":{"node":">=20.11"},"type":"module","bin":{"builder":"./bin/builder.mjs"},"peerDependencies":{"@types/node":">=20.11","esbuild":"0.20.0","typescript":"5.4.0-beta"},"repository":{"type":"git","url":"git+https://github.com/checkdigit/typescript-config.git"},"author":"Check Digit, LLC","license":"MIT","bugs":{"url":"https://github.com/checkdigit/typescript-config/issues"},"homepage":"https://github.com/checkdigit/typescript-config#readme","scripts":{"prepublishOnly":"npm run build-builder","lint:fix":"eslint --ignore-path .gitignore . --fix","lint":"eslint --max-warnings 0 --ignore-path .gitignore .","prettier":"prettier --ignore-path .gitignore --list-different .","prettier:fix":"prettier --ignore-path .gitignore --write .","test":"npm run ci:compile && npm run ci:test && npm run ci:lint && npm run ci:style","build-builder":"esbuild src/builder.ts --bundle --platform=node --format=esm --external:typescript --external:esbuild --outfile=build-builder/builder.mjs && mkdir -p bin && { echo '#!/usr/bin/env node'; cat build-builder/builder.mjs; } > bin/builder.mjs && chmod +x bin/builder.mjs","build-types":"rimraf build-types && bin/builder.mjs --type=types --outDir=build-types","build-mjs":"rimraf build-mjs && bin/builder.mjs --type=module --outDir=build-mjs","build-mjs-bundle":"rimraf build-mjs-bundle && bin/builder.mjs --type=module --outDir=build-mjs-bundle --entryPoint=test/index.test.ts --outFile=test/index.test.mjs","build-mjs-bundle-minify":"rimraf build-mjs-bundle-minify && bin/builder.mjs --type=module --minify --outDir=build-mjs-bundle-minify --entryPoint=test/index.test.ts --outFile=test/index.test.mjs","build-mjs-bundle-no-external":"rimraf build-mjs-bundle-no-external && bin/builder.mjs --type=module --external=./node_modules/* --outDir=build-mjs-bundle-no-external --entryPoint=test/index.test.ts --outFile=test/index.test.mjs --minify","test-jest-mjs":"NODE_OPTIONS=\"--disable-warning ExperimentalWarning --experimental-vm-modules\" jest --coverage=false","test-mjs":"node --test build-mjs/test/index.test.mjs","test-mjs-bundle":"node --test build-mjs-bundle/test/index.test.mjs","test-mjs-bundle-minify":"node --test build-mjs-bundle-minify/test/index.test.mjs","test-mjs-bundle-no-external":"node --test build-mjs-bundle-no-external/test/index.test.mjs","ci:test":"npm run test-jest-mjs && npm run test-mjs && npm run test-mjs-bundle && npm run test-mjs-bundle-no-external","ci:compile":"tsc --noEmit && npm run build-builder && npm run build-types && npm run build-mjs && npm run build-mjs-bundle && npm run build-mjs-bundle-minify && npm run build-mjs-bundle-no-external","ci:lint":"npm run lint","ci:style":"npm run prettier"},"devDependencies":{"@apidevtools/json-schema-ref-parser":"^11.1.0","@checkdigit/prettier-config":"^5.2.0","@types/debug":"^4.1.12","@types/jest":"^29.5.11","@types/uuid":"^9.0.8","@typescript-eslint/eslint-plugin":"^6.20.0","@typescript-eslint/parser":"^6.20.0","debug":"^4.3.4","eslint":"^8.56.0","eslint-config-prettier":"^9.1.0","jest":"^29.7.0","node-fetch":"^3.3.2","rimraf":"^5.0.5","ts-jest":"^29.1.2","uuid":"^9.0.1"},"eslintConfig":{"parser":"@typescript-eslint/parser","plugins":["@typescript-eslint"],"parserOptions":{"project":"./tsconfig.json"},"extends":["eslint:all","plugin:@typescript-eslint/recommended","plugin:@typescript-eslint/recommended-requiring-type-checking","plugin:@typescript-eslint/strict","prettier"],"rules":{"@typescript-eslint/non-nullable-type-assertion-style":"error","capitalized-comments":"off","one-var":"off","sort-keys":"off","sort-imports":"off","max-lines":["error",{"max":500,"skipBlankLines":true,"skipComments":true}],"func-style":["error","declaration",{"allowArrowFunctions":true}],"no-magic-numbers":["error",{"ignore":[0,1,2]}],"no-undefined":"off","no-ternary":"off"},"overrides":[{"files":["*.spec.ts","*.test.ts"],"rules":{"@typescript-eslint/non-nullable-type-assertion-style":"off","@typescript-eslint/ban-types":"off","@typescript-eslint/require-await":"off","@typescript-eslint/consistent-type-definitions":"off","@typescript-eslint/ban-ts-comment":"off","@typescript-eslint/no-unnecessary-condition":"off","@typescript-eslint/consistent-indexed-object-style":"off","@typescript-eslint/no-unused-vars":"off","@typescript-eslint/no-unsafe-member-access":"off","line-comment-position":"off","no-fallthrough":"off","no-inline-comments":"off","no-param-reassign":"off","id-length":"off","no-magic-numbers":"off","func-names":"off","no-duplicate-imports":"off","symbol-description":"off","no-invalid-this":"off","max-lines-per-function":"off","max-lines":"off","max-statements":"off","no-await-in-loop":"off"}}]},"jest":{"moduleFileExtensions":["js","mjs","cjs","ts","json","node"],"extensionsToTreatAsEsm":[".ts"],"transform":{"^.+\\.ts$":["ts-jest",{"isolatedModules":true,"diagnostics":false,"useESM":true}]},"collectCoverageFrom":["<rootDir>/src/**"],"testMatch":["<rootDir>/src/**/*.spec.ts"]},"files":["bin","tsconfig.json","SECURITY.md"],"overrides":{"typescript":"5.4.0-beta"}}
1
+ {"name":"@checkdigit/typescript-config","version":"7.0.0-PR.54-2621","description":"Check Digit standard Typescript configuration","prettier":"@checkdigit/prettier-config","engines":{"node":">=20.11"},"type":"module","exports":{".":{"types":"./dist-types/index.d.ts","import":"./dist-mjs/index.mjs","default":"./dist-mjs/index.mjs"}},"bin":{"builder":"./bin/builder.mjs"},"peerDependencies":{"@types/node":">=20.11","esbuild":"0.20.0","typescript":"5.4.0-beta"},"repository":{"type":"git","url":"git+https://github.com/checkdigit/typescript-config.git"},"author":"Check Digit, LLC","license":"MIT","bugs":{"url":"https://github.com/checkdigit/typescript-config/issues"},"homepage":"https://github.com/checkdigit/typescript-config#readme","scripts":{"prepublishOnly":"npm run build-builder && npm run build:dist-types && npm run build:dist-mjs","build:dist-types":"rimraf dist-types && npx builder --type=types --outDir=dist-types","build:dist-mjs":"rimraf dist-mjs && npx builder --type=module --sourceMap --outDir=dist-mjs && node dist-mjs/index.mjs","lint:fix":"eslint --ignore-path .gitignore . --fix","lint":"eslint --max-warnings 0 --ignore-path .gitignore .","prettier":"prettier --ignore-path .gitignore --list-different .","prettier:fix":"prettier --ignore-path .gitignore --write .","test":"npm run ci:compile && npm run ci:test && npm run ci:lint && npm run ci:style","build-builder":"esbuild src/builder.ts --bundle --platform=node --format=esm --external:typescript --external:esbuild --outfile=build-builder/builder.mjs && mkdir -p bin && { echo '#!/usr/bin/env node'; cat build-builder/builder.mjs; } > bin/builder.mjs && chmod +x bin/builder.mjs","build-types":"rimraf build-types && bin/builder.mjs --type=types --outDir=build-types","build-mjs":"rimraf build-mjs && bin/builder.mjs --type=module --outDir=build-mjs","build-mjs-bundle":"rimraf build-mjs-bundle && bin/builder.mjs --type=module --outDir=build-mjs-bundle --entryPoint=test/index.test.ts --outFile=test/index.test.mjs","build-mjs-bundle-minify":"rimraf build-mjs-bundle-minify && bin/builder.mjs --type=module --minify --outDir=build-mjs-bundle-minify --entryPoint=test/index.test.ts --outFile=test/index.test.mjs","build-mjs-bundle-no-external":"rimraf build-mjs-bundle-no-external && bin/builder.mjs --type=module --external=./node_modules/* --outDir=build-mjs-bundle-no-external --entryPoint=test/index.test.ts --outFile=test/index.test.mjs --minify","test-jest-mjs":"NODE_OPTIONS=\"--disable-warning ExperimentalWarning --experimental-vm-modules\" jest --coverage=false","test-mjs":"node --test build-mjs/test/index.test.mjs","test-mjs-bundle":"node --test build-mjs-bundle/test/index.test.mjs","test-mjs-bundle-minify":"node --test build-mjs-bundle-minify/test/index.test.mjs","test-mjs-bundle-no-external":"node --test build-mjs-bundle-no-external/test/index.test.mjs","ci:test":"npm run test-jest-mjs && npm run test-mjs && npm run test-mjs-bundle && npm run test-mjs-bundle-no-external","ci:compile":"tsc --noEmit && npm run build-builder && npm run build-types && npm run build-mjs && npm run build-mjs-bundle && npm run build-mjs-bundle-minify && npm run build-mjs-bundle-no-external","ci:lint":"npm run lint","ci:style":"npm run prettier"},"devDependencies":{"@apidevtools/json-schema-ref-parser":"^11.1.0","@checkdigit/prettier-config":"^5.2.0","@types/debug":"^4.1.12","@types/jest":"^29.5.11","@types/uuid":"^9.0.8","@typescript-eslint/eslint-plugin":"^6.20.0","@typescript-eslint/parser":"^6.20.0","debug":"^4.3.4","eslint":"^8.56.0","eslint-config-prettier":"^9.1.0","jest":"^29.7.0","node-fetch":"^3.3.2","rimraf":"^5.0.5","ts-jest":"^29.1.2","uuid":"^9.0.1"},"eslintConfig":{"parser":"@typescript-eslint/parser","plugins":["@typescript-eslint"],"parserOptions":{"project":"./tsconfig.json"},"extends":["eslint:all","plugin:@typescript-eslint/recommended","plugin:@typescript-eslint/recommended-requiring-type-checking","plugin:@typescript-eslint/strict","prettier"],"rules":{"@typescript-eslint/non-nullable-type-assertion-style":"error","capitalized-comments":"off","one-var":"off","sort-keys":"off","sort-imports":"off","max-lines":["error",{"max":500,"skipBlankLines":true,"skipComments":true}],"func-style":["error","declaration",{"allowArrowFunctions":true}],"no-magic-numbers":["error",{"ignore":[0,1,2]}],"no-undefined":"off","no-ternary":"off"},"overrides":[{"files":["*.spec.ts","*.test.ts"],"rules":{"@typescript-eslint/non-nullable-type-assertion-style":"off","@typescript-eslint/ban-types":"off","@typescript-eslint/require-await":"off","@typescript-eslint/consistent-type-definitions":"off","@typescript-eslint/ban-ts-comment":"off","@typescript-eslint/no-unnecessary-condition":"off","@typescript-eslint/consistent-indexed-object-style":"off","@typescript-eslint/no-unused-vars":"off","@typescript-eslint/no-unsafe-member-access":"off","line-comment-position":"off","no-fallthrough":"off","no-inline-comments":"off","no-param-reassign":"off","id-length":"off","no-magic-numbers":"off","func-names":"off","no-duplicate-imports":"off","symbol-description":"off","no-invalid-this":"off","max-lines-per-function":"off","max-lines":"off","max-statements":"off","no-await-in-loop":"off"}}]},"jest":{"moduleFileExtensions":["js","mjs","cjs","ts","json","node"],"extensionsToTreatAsEsm":[".ts"],"transform":{"^.+\\.ts$":["ts-jest",{"isolatedModules":true,"diagnostics":false,"useESM":true}]},"collectCoverageFrom":["<rootDir>/src/**"],"testMatch":["<rootDir>/src/**/*.spec.ts"]},"files":["bin","tsconfig.json","src","dist-types","dist-mjs","!src/**/*.test.ts","!src/**/*.spec.ts","!dist-types/**/*.test.d.ts","!dist-types/**/*.spec.d.ts","!dist-mjs/**/*.test.mjs","!dist-mjs/**/*.spec.mjs","SECURITY.md"],"overrides":{"typescript":"5.4.0-beta"}}
package/src/analyze.ts ADDED
@@ -0,0 +1,34 @@
1
+ // analyze.ts
2
+
3
+ import { strict as assert } from 'node:assert';
4
+
5
+ import type { Metafile } from 'esbuild';
6
+
7
+ export default function analyze(metafile: Metafile) {
8
+ const source = new Set(Object.keys(metafile.inputs).filter((key) => !key.startsWith('node_modules')));
9
+ const modules = new Set(Object.keys(metafile.inputs).filter((key) => key.startsWith('node_modules')));
10
+
11
+ const [output] = Object.entries(metafile.outputs);
12
+ assert.ok(output !== undefined);
13
+ const [, bundle] = output;
14
+
15
+ const sourceBytes = Object.entries(bundle.inputs).reduce((bytes, [file, value]) => {
16
+ if (source.has(file)) {
17
+ return bytes + value.bytesInOutput;
18
+ }
19
+ return bytes;
20
+ }, 0);
21
+
22
+ const moduleBytes = Object.entries(bundle.inputs).reduce((bytes, [file, value]) => {
23
+ if (modules.has(file)) {
24
+ return bytes + value.bytesInOutput;
25
+ }
26
+ return bytes;
27
+ }, 0);
28
+
29
+ return {
30
+ sourceBytes,
31
+ moduleBytes,
32
+ totalBytes: bundle.bytes,
33
+ };
34
+ }
package/src/builder.ts ADDED
@@ -0,0 +1,57 @@
1
+ // builder.ts
2
+
3
+ import { strict as assert } from 'node:assert';
4
+ import { promises as fs } from 'node:fs';
5
+ import path from 'node:path';
6
+ import { parseArgs } from 'node:util';
7
+
8
+ import { compile, analyze } from './index';
9
+
10
+ const {
11
+ values: { type, inDir, outDir, entryPoint, outFile, external, minify, sourceMap },
12
+ } = parseArgs({
13
+ options: {
14
+ type: { type: 'string', short: 't', default: 'module' },
15
+ inDir: { type: 'string', short: 'i', default: 'src' },
16
+ outDir: { type: 'string', short: 'o', default: 'build' },
17
+ entryPoint: { type: 'string', short: 'e', default: undefined },
18
+ outFile: { type: 'string', short: 'f', default: undefined },
19
+ external: { type: 'string', short: 'x', multiple: true, default: [] },
20
+ minify: { type: 'boolean', short: 'm', default: false },
21
+ sourceMap: { type: 'boolean', short: 's', default: false },
22
+ },
23
+ });
24
+
25
+ assert.ok(type === 'module' || type === 'types', 'type must be types or module');
26
+ assert.ok(inDir !== undefined, 'inDir is required');
27
+ assert.ok(outDir !== undefined, 'outDir is required');
28
+
29
+ const compileResult = await compile({
30
+ type,
31
+ inDir: path.join(process.cwd(), inDir),
32
+ outDir: path.join(process.cwd(), outDir),
33
+ entryPoint,
34
+ outFile,
35
+ external,
36
+ minify,
37
+ sourceMap,
38
+ });
39
+
40
+ // write output files
41
+ await Promise.all(
42
+ compileResult.outputFiles.map(async (file) => {
43
+ await fs.mkdir(path.join(path.dirname(file.path)), { recursive: true });
44
+ await fs.writeFile(file.path, file.text);
45
+ }),
46
+ );
47
+
48
+ // write metafile.json
49
+ if (compileResult.metafile !== undefined) {
50
+ const analysis = analyze(compileResult.metafile);
51
+ await fs.writeFile(path.join(outDir, 'metafile.json'), JSON.stringify(compileResult.metafile, undefined, 2));
52
+
53
+ // eslint-disable-next-line no-console
54
+ console.log(
55
+ `${outFile}: src ${analysis.sourceBytes}, node_modules ${analysis.moduleBytes}, total ${analysis.totalBytes}`,
56
+ );
57
+ }
package/src/compile.ts ADDED
@@ -0,0 +1,320 @@
1
+ // compile.ts
2
+
3
+ import { strict as assert } from 'node:assert';
4
+ import { promises as fs } from 'node:fs';
5
+ import path from 'node:path';
6
+
7
+ import typescript from 'typescript';
8
+ import { type PluginBuild, build } from 'esbuild';
9
+
10
+ const commonJsCompatabilityBanner = `import { createRequire as __createRequire } from "node:module";
11
+ import { fileURLToPath as __fileURLToPath } from "node:url";
12
+ import { default as __path } from "node:path";
13
+ const __filename = __fileURLToPath(import.meta.url);
14
+ const __dirname = __path.dirname(__filename);
15
+ const require = __createRequire(import.meta.url);`;
16
+
17
+ export type ImportKind =
18
+ | 'entry-point'
19
+ | 'import-statement'
20
+ | 'require-call'
21
+ | 'dynamic-import'
22
+ | 'require-resolve'
23
+ | 'import-rule'
24
+ | 'composes-from'
25
+ | 'url-token';
26
+
27
+ export interface Metafile {
28
+ inputs: {
29
+ [path: string]: {
30
+ bytes: number;
31
+ imports: {
32
+ path: string;
33
+ kind: ImportKind;
34
+ external?: boolean;
35
+ original?: string;
36
+ }[];
37
+ format?: 'cjs' | 'esm';
38
+ };
39
+ };
40
+ outputs: {
41
+ [path: string]: {
42
+ bytes: number;
43
+ inputs: {
44
+ [path: string]: {
45
+ bytesInOutput: number;
46
+ };
47
+ };
48
+ imports: {
49
+ path: string;
50
+ kind: ImportKind | 'file-loader';
51
+ external?: boolean;
52
+ }[];
53
+ exports: string[];
54
+ entryPoint?: string;
55
+ cssBundle?: string;
56
+ };
57
+ };
58
+ }
59
+
60
+ export interface OutputFile {
61
+ path: string;
62
+ text: string;
63
+ }
64
+
65
+ export interface CompileResult {
66
+ metafile?: Metafile | undefined;
67
+ outputFiles: OutputFile[];
68
+ }
69
+
70
+ export interface CompileOptions {
71
+ /**
72
+ * whether to produce Typescript types or ESM code
73
+ */
74
+ type: 'module' | 'types';
75
+
76
+ /**
77
+ * the entry point for the bundle, relative to the inDir. if not provided, the files in the inDir will be processed
78
+ * as individual unbundled files
79
+ */
80
+ entryPoint?: string | undefined;
81
+
82
+ /**
83
+ * source code
84
+ */
85
+ inDir: string;
86
+
87
+ /**
88
+ * build directory
89
+ */
90
+ outDir: string;
91
+
92
+ /**
93
+ * build file, relative to the outDir
94
+ */
95
+ outFile?: string | undefined;
96
+
97
+ /**
98
+ * external modules to exclude from the bundle
99
+ */
100
+ external?: string[] | undefined;
101
+
102
+ /**
103
+ * whether to minify output
104
+ */
105
+ minify?: boolean | undefined;
106
+
107
+ /**
108
+ * whether to include sourcemap
109
+ */
110
+ sourceMap?: boolean | undefined;
111
+
112
+ /**
113
+ * working directory
114
+ */
115
+ workingDirectory?: string | undefined;
116
+ }
117
+
118
+ /**
119
+ * Recursively obtains all files in a directory
120
+ * @param {string} directory
121
+ * @returns {Promise<string[]>}
122
+ */
123
+ async function getFiles(directory: string): Promise<string[]> {
124
+ const entries = await fs.readdir(directory, { withFileTypes: true });
125
+ const files = await Promise.all(
126
+ entries.map((entry) => {
127
+ const result = path.resolve(directory, entry.name);
128
+ return entry.isDirectory() ? getFiles(result) : result;
129
+ }),
130
+ );
131
+ return files.flat();
132
+ }
133
+
134
+ function excludeSourceMaps(filter: RegExp) {
135
+ return (pluginBuild: PluginBuild) => {
136
+ // ignore source maps for any Javascript file that matches filter
137
+ pluginBuild.onLoad({ filter }, async (args) => {
138
+ if (args.path.endsWith('.js') || args.path.endsWith('.mjs')) {
139
+ return {
140
+ contents: `${await fs.readFile(
141
+ args.path,
142
+ 'utf8',
143
+ )}\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIiJdLCJtYXBwaW5ncyI6IkEifQ==`,
144
+ loader: 'default',
145
+ };
146
+ }
147
+ return undefined;
148
+ });
149
+ };
150
+ }
151
+
152
+ function resolveTypescriptPaths() {
153
+ return (pluginBuild: PluginBuild) => {
154
+ // rewrite paths based on standard node resolution
155
+ pluginBuild.onResolve({ filter: /.*/u }, async (resolved) => {
156
+ if (
157
+ resolved.kind === 'entry-point' ||
158
+ !resolved.path.startsWith('.') ||
159
+ resolved.path.endsWith('.js') ||
160
+ resolved.path.endsWith('.json')
161
+ ) {
162
+ return { external: resolved.kind !== 'entry-point' };
163
+ }
164
+ let isDirectory = false;
165
+ try {
166
+ const stats = await fs.lstat(path.join(resolved.resolveDir, resolved.path));
167
+ isDirectory = stats.isDirectory();
168
+ } catch {
169
+ // do nothing
170
+ }
171
+ let newPath = resolved.path;
172
+ newPath += isDirectory ? `/index.mjs` : `.mjs`;
173
+ return { path: newPath, external: true };
174
+ });
175
+ };
176
+ }
177
+
178
+ // eslint-disable-next-line func-names,max-lines-per-function,max-statements
179
+ export default async function ({
180
+ type,
181
+ entryPoint,
182
+ inDir,
183
+ outDir,
184
+ outFile,
185
+ external = [],
186
+ minify = false,
187
+ sourceMap,
188
+ workingDirectory = process.cwd(),
189
+ }: CompileOptions): Promise<CompileResult> {
190
+ const messages: string[] = [];
191
+
192
+ assert.ok(
193
+ (entryPoint === undefined && outFile === undefined) || (entryPoint !== undefined && outFile !== undefined),
194
+ 'entryPoint and outFile must both be provided',
195
+ );
196
+
197
+ const allSourceFiles = await getFiles(inDir);
198
+ const productionSourceFiles =
199
+ entryPoint === undefined ? allSourceFiles.filter((file) => file.endsWith('.ts')) : [path.join(inDir, entryPoint)];
200
+
201
+ /**
202
+ * Emit declarations using typescript compiler, if type is 'types'. Otherwise, just compile to ensure the build is good.
203
+ */
204
+ const program = typescript.createProgram(productionSourceFiles, {
205
+ module: typescript.ModuleKind.ESNext,
206
+ moduleResolution: typescript.ModuleResolutionKind.Bundler,
207
+ target: typescript.ScriptTarget.ESNext,
208
+ declaration: true,
209
+ noEmit: type !== 'types',
210
+ noEmitOnError: true,
211
+ emitDeclarationOnly: type === 'types',
212
+ rootDir: inDir,
213
+ outDir,
214
+ noLib: false,
215
+ skipLibCheck: true,
216
+ strict: true,
217
+ preserveConstEnums: true,
218
+ noImplicitReturns: true,
219
+ noUnusedLocals: true,
220
+ noUnusedParameters: true,
221
+ alwaysStrict: true,
222
+ verbatimModuleSyntax: true,
223
+ forceConsistentCasingInFileNames: true,
224
+ emitDecoratorMetadata: true,
225
+ experimentalDecorators: true,
226
+ resolveJsonModule: true,
227
+ esModuleInterop: true,
228
+ noUncheckedIndexedAccess: true,
229
+ noPropertyAccessFromIndexSignature: true,
230
+ allowUnusedLabels: false,
231
+ allowUnreachableCode: false,
232
+ noImplicitOverride: true,
233
+ useUnknownInCatchVariables: true,
234
+ exactOptionalPropertyTypes: true,
235
+ });
236
+ const declarationFiles: OutputFile[] = [];
237
+ const emitResult = program.emit(undefined, (fileName, data) => {
238
+ declarationFiles.push({ path: fileName, text: data });
239
+ });
240
+ const allDiagnostics = typescript.sortAndDeduplicateDiagnostics([
241
+ ...typescript.getPreEmitDiagnostics(program),
242
+ ...emitResult.diagnostics,
243
+ ]);
244
+ for (const diagnostic of allDiagnostics) {
245
+ if (diagnostic.file) {
246
+ assert.ok(diagnostic.start !== undefined);
247
+ const { line, character } = typescript.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
248
+ const message = typescript.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
249
+ messages.push(`tsc: ${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
250
+ } else {
251
+ // eslint-disable-next-line no-console
252
+ messages.push(`tsc: ${typescript.flattenDiagnosticMessageText(diagnostic.messageText, '\n')}`);
253
+ }
254
+ }
255
+
256
+ if (messages.length > 0) {
257
+ throw new Error(`tsc failed ${JSON.stringify(messages)}`);
258
+ }
259
+
260
+ if (type === 'types') {
261
+ return {
262
+ outputFiles: declarationFiles,
263
+ };
264
+ }
265
+
266
+ /**
267
+ * Emit ESM javascript using esbuild
268
+ */
269
+ const buildResult = await build({
270
+ entryPoints: productionSourceFiles,
271
+ bundle: true,
272
+ minify,
273
+ absWorkingDir: workingDirectory,
274
+ platform: 'node',
275
+ format: 'esm',
276
+ treeShaking: type === 'module',
277
+ write: false,
278
+ metafile: outFile !== undefined,
279
+ sourcesContent: false,
280
+ banner:
281
+ type === 'module' && outFile !== undefined
282
+ ? {
283
+ js: commonJsCompatabilityBanner,
284
+ }
285
+ : {},
286
+ sourcemap: sourceMap ? 'inline' : false,
287
+ ...(outFile === undefined
288
+ ? {
289
+ // individual files
290
+ outdir: outDir,
291
+ outExtension: { '.js': '.mjs' },
292
+ plugins: [
293
+ {
294
+ name: 'resolve-typescript-paths',
295
+ setup: resolveTypescriptPaths(),
296
+ },
297
+ ],
298
+ }
299
+ : {
300
+ // bundling
301
+ outfile: path.join(outDir, outFile),
302
+ legalComments: 'none',
303
+ external,
304
+ plugins: [
305
+ {
306
+ name: 'exclude-source-maps',
307
+ setup: excludeSourceMaps(/node_modules/u),
308
+ },
309
+ ],
310
+ }),
311
+ });
312
+
313
+ messages.push(...buildResult.errors.map((error) => `esbuild error: ${error.text}`));
314
+ messages.push(...buildResult.warnings.map((warning) => `esbuild warning: ${warning.text}`));
315
+ if (messages.length > 0) {
316
+ throw new Error(`esbuild failed ${JSON.stringify(messages)}`);
317
+ }
318
+
319
+ return buildResult;
320
+ }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ // index.ts
2
+
3
+ export * from './analyze';
4
+ export * from './compile';
5
+
6
+ export { default as analyze } from './analyze';
7
+ export { default as compile } from './compile';
@@ -0,0 +1,3 @@
1
+ // test/module-directory/index.ts
2
+
3
+ export default () => 'module-directory-index';