Skip to content

Commit b31d9c0

Browse files
committed
f
1 parent 1050866 commit b31d9c0

8 files changed

Lines changed: 1927 additions & 1840 deletions

File tree

TRANSPILATION.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ There is one more transpiler just for one use case to work with node.
2828
# TODO
2929
modify bundle.sh to take *.entry.ts too
3030

31+
remove esbuild-entries.js
32+
33+
3134

3235

3336

bundle.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ find . -type d \( \
2929
-o -name noprettier \
3030
-o -name .opencode \
3131
\) -prune \
32-
-o -type \
33-
f \( -name "*.entry.js" -o -name "*.entry.jsx" \) \
32+
-o -type f \
33+
\( -name "*.entry.js" -o -name "*.entry.jsx" \) \
3434
-print \
3535
| node gitignore.js "${IGNORE_FILE}" \
3636
| npx tsx bundle.ts "$@"

es.ignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
# gitignore for es.sh
22
*
33
!es.ts
4-
!pages/node/semaphore/Semaphore.ts
4+
!pages/node/semaphore/Semaphore.ts
5+
!pages/bash/xx/xx.node.cjs
6+
!pages/bash/xx/xx.lock.gits-update-config.node.cjs

es.sh

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@
88

99
set -e
1010

11-
export NODE_OPTIONS=""
1211

13-
find . \
14-
-path './node_modules' -prune -o \
15-
-path './.git' -prune -o \
16-
-path './.opencode' -prune -o \
17-
-path './noprettier' -prune -o \
18-
-path './scripts' -prune -o \
19-
-type f -name '*.ts' \
20-
-print \
21-
| node gitignore.js es.ignore \
22-
| DEBUG=true /bin/bash ts.sh es.ts --produce-gitignore --update
12+
13+
find . -type d \( \
14+
-name node_modules \
15+
-o -name .git \
16+
-o -name coverage \
17+
-o -name noprettier \
18+
-o -name scripts \
19+
-o -name .opencode \
20+
\) -prune \
21+
-o -type f \
22+
\( -name '*.ts' -o -name "*.node.cjs" \) \
23+
-print \
24+
| NODE_OPTIONS="" node gitignore.js es.ignore \
25+
| DEBUG=true /bin/bash ts.sh es.ts --produce-gitignore --update

es.ts

Lines changed: 90 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,17 @@ import Semaphore from "./pages/node/semaphore/Semaphore.ts";
1919

2020
const th = (msg: string) => new Error(`es.ts error: ${msg}`);
2121

22-
interface Config extends Pick<esbuild.BuildOptions, "target" | "charset" | "minify" | "bundle" | "format"> {
22+
interface InternalConfig {
23+
target: string;
2324
loader: esbuild.Loader;
25+
format: "esm" | "cjs" | "iife";
26+
charset: "utf8" | "ascii";
27+
minify: boolean;
28+
bundle: boolean;
2429
extension: string;
2530
}
2631

27-
const CONFIG: Config = {
32+
const CONFIG: InternalConfig = {
2833
target: "esnext",
2934
loader: "ts",
3035
format: "esm",
@@ -92,8 +97,7 @@ async function stripTypes(filePath: string): Promise<string | undefined> {
9297
const startMarker = "/** @es.ts";
9398
const endMarker = `@es.ts *${SLASH}`;
9499

95-
let buildMode: "bundle" | "transform" = CONFIG.bundle ? "bundle" : "transform";
96-
let localOptions: any = { ...CONFIG };
100+
let config: any = {};
97101

98102
const startIndex = source.indexOf(startMarker);
99103
const endIndex = source.indexOf(endMarker);
@@ -102,40 +106,53 @@ async function stripTypes(filePath: string): Promise<string | undefined> {
102106
const configStr = source.substring(startIndex + startMarker.length, endIndex).trim();
103107
try {
104108
// Using Function to safely parse the object literal (allows unquoted keys)
105-
const config = new Function(`return (${configStr})`)();
106-
if (config.mode) {
107-
if (config.mode !== "bundle" && config.mode !== "transform") {
108-
throw th(`Invalid mode "${config.mode}" in ${filePath}. Only "bundle" or "transform" are allowed.`);
109-
}
110-
buildMode = config.mode;
111-
}
112-
if (config.extension) {
113-
localOptions.extension = config.extension;
114-
}
115-
if (config.options) {
116-
localOptions = { ...localOptions, ...config.options };
117-
}
109+
config = new Function(`return (${configStr})`)();
118110
} catch (e: any) {
119-
if (e.message.includes('Invalid mode "')) {
120-
throw e;
121-
}
122111
console.error(`Error parsing @es.ts config in ${filePath}: ${e.message}`);
123112
}
124113
}
125114

126-
const outPath: string = join(dirname(filePath), basename(filePath).replace(/\.ts$/, localOptions.extension));
115+
const buildMode = config.mode || (CONFIG.bundle ? "bundle" : "transform");
116+
const extension = config.extension || CONFIG.extension;
117+
const loader = config.loader || CONFIG.loader;
127118

128-
const options: esbuild.BuildOptions = {
129-
entryPoints: [filePath],
119+
// 1. Prepare base options from defaults
120+
let baseOptions: esbuild.BuildOptions = {
121+
target: CONFIG.target as any,
122+
charset: CONFIG.charset,
123+
minify: CONFIG.minify,
130124
bundle: buildMode === "bundle",
131-
write: false,
132-
target: localOptions.target as any,
133-
charset: localOptions.charset,
134-
minify: localOptions.minify,
135-
legalComments: "inline",
125+
format: CONFIG.format,
136126
platform: "node",
137-
format: localOptions.format,
127+
legalComments: "inline",
128+
};
129+
130+
// 2. If 'setup' is provided, it replaces the base options
131+
if (config.setup) {
132+
baseOptions = { ...config.setup };
133+
}
134+
135+
// 3. Merge top-level config fields (excluding tool-specific ones)
136+
const { mode, extension: _ext, setup, options, ...rest } = config;
137+
const mergedOptions: any = {
138+
...baseOptions,
139+
...rest,
140+
};
141+
142+
// Allow unsetting defaults by passing undefined
143+
Object.keys(mergedOptions).forEach((key) => {
144+
if (mergedOptions[key] === undefined) {
145+
delete mergedOptions[key];
146+
}
147+
});
148+
149+
// 4. Force essential options and inject plugin
150+
const finalOptions: esbuild.BuildOptions = {
151+
...mergedOptions,
152+
entryPoints: [filePath],
153+
write: false,
138154
plugins: [
155+
...(mergedOptions.plugins || []),
139156
{
140157
name: "protect-comments",
141158
setup(build) {
@@ -144,35 +161,49 @@ async function stripTypes(filePath: string): Promise<string | undefined> {
144161
const contents = content
145162
.replace(/\/\*\*/g, "/*!") // JSDoc -> Legal block
146163
.replace(/\/\/ /g, "//! "); // Single line -> Legal line
147-
return { contents, loader: localOptions.loader };
164+
return { contents, loader };
148165
});
149166
},
150167
},
151168
],
152169
};
153170

171+
// 5. Handle output path defaulting if not specified
172+
if (!finalOptions.outfile && !finalOptions.outdir) {
173+
finalOptions.outfile = join(dirname(filePath), basename(filePath).replace(/\.ts$/, extension));
174+
}
175+
154176
if (env.DEBUG) {
155-
console.log(JSON.stringify(options, null, 2));
177+
console.log(`Final esbuild options for ${filePath}:`);
178+
console.log(JSON.stringify(finalOptions, null, 2));
156179
}
157180

158-
const result = await esbuild.build(options);
181+
const result = await esbuild.build(finalOptions);
182+
183+
let firstOutPath: string | undefined;
159184

160-
if (result.outputFiles && result.outputFiles[0]) {
161-
let outputText: string = result.outputFiles[0].text;
185+
if (result.outputFiles) {
186+
for (const file of result.outputFiles) {
187+
let outputText: string = file.text;
162188

163-
// Restore protected comments
164-
outputText = outputText
165-
.replace(/\/\*\!/g, "/**")
166-
.replace(/\/\/! /g, "// ")
167-
.replace(/(@es\.ts \*\/\s*)/g, `@es.ts *${SLASH}`);
189+
// Restore protected comments
190+
outputText = outputText
191+
.replace(/\/\*\!/g, "/**")
192+
.replace(/\/\/! /g, "// ")
193+
.replace(/(@es\.ts \*\/\s*)/g, `@es.ts *${SLASH}`);
168194

169-
writeFileSync(outPath, outputText);
195+
writeFileSync(file.path, outputText);
196+
197+
if (!firstOutPath) {
198+
firstOutPath = file.path;
199+
}
200+
}
170201
}
171202

172-
if (!PRODUCE_GITIGNORE) {
173-
console.log(`${buildMode === "bundle" ? "Bundled" : "Transpiled"} (esbuild): ${filePath} -> ${outPath}`);
203+
if (!PRODUCE_GITIGNORE && firstOutPath) {
204+
console.log(`${buildMode === "bundle" ? "Bundled" : "Transpiled"} (esbuild): ${filePath} -> ${firstOutPath}`);
174205
}
175-
return outPath;
206+
return firstOutPath;
176207
} catch (err: unknown) {
177208
hasError = true;
178209
const message = err instanceof Error ? err.message : String(err);
@@ -267,12 +298,19 @@ Description:
267298
Add this block to a .ts file to override default behavior:
268299
/** @es.ts
269300
{
270-
mode: "bundle|transform",
271-
extension: ".js|.mjs",
272-
options: {
273-
target: "esnext", loader: "ts",
274-
charset: "utf8", minify: false
275-
}
301+
// Tool-specific keys:
302+
mode: "bundle|transform", // optional, shorthand for bundle: true/false
303+
extension: ".js|.mjs", // optional, default output extension
304+
setup: { ... }, // optional, if present replaces all default esbuild options
305+
306+
// Any other keys are treated as esbuild BuildOptions and merged with defaults:
307+
target: "esnext",
308+
minify: false,
309+
platform: "node",
310+
// ...
311+
312+
// Use 'undefined' to unset a default and let esbuild decide:
313+
minify: undefined,
276314
}
277315
@es.ts *${SLASH}
278316
@@ -281,7 +319,7 @@ Description:
281319
to esbuild.transform (input and options) are dumped to the
282320
console for each processed file.
283321
284-
Built-in Config:
322+
Built-in Config (generated internally - so it is true setup which will be really used):
285323
${JSON.stringify(CONFIG, null, 2)}
286324
`);
287325
}
@@ -311,8 +349,9 @@ for await (const line of rl) {
311349
// Start the task and keep track of it in the activeTasks Set
312350
const task: Promise<void> = (async () => {
313351
try {
314-
const outPath = await stripTypes(file);
352+
let outPath = await stripTypes(file);
315353
if (PRODUCE_GITIGNORE && outPath) {
354+
outPath = relative(gitRoot, outPath);
316355
gitignorePaths.push(outPath);
317356
}
318357
} finally {

pages/bash/xx/xx.lock.gits-update-config.node.cjs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
/** @es.ts
2+
{
3+
mode: "bundle",
4+
bundle: true,
5+
outdir: ".",
6+
outbase: ".",
7+
entryNames: "[dir]/[name].bundled.gitignored",
8+
platform: "node",
9+
"format": "cjs",
10+
target: "node12.16.3",
11+
logLevel: "info",
12+
logOverride: {
13+
"direct-eval": "silent",
14+
},
15+
outExtension: {
16+
".js": ".cjs",
17+
},
18+
options: {
19+
}
20+
}
21+
@es.ts */
122
/**
223
* xx --lock
324
* node "${_DIR}/xx.lock.gits-update-config.node.bundled.gitignored.cjs" "${GITIGNORE}" "${_PWD}/.git/.gitignore_local"

0 commit comments

Comments
 (0)