@checkdigit/typescript-config 7.0.0-PR.54-eb75 → 7.0.0-PR.54-950f

Sign up to get free protection for your applications and to get access to all the features.
@@ -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-eb75","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-950f","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":"./tsconfig.json"}},"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';