@decohere/forge 0.0.1-dev.111 → 0.0.1-dev.112
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/commonjs/src/services/swc-transpiler.js +9 -1
- package/commonjs/src/vitest/forge-build.plugin.js +21 -30
- package/es6/src/services/swc-transpiler.js +9 -1
- package/es6/src/vitest/forge-build.plugin.js +21 -30
- package/package.json +5 -5
- package/types/src/services/swc-transpiler.d.ts +6 -0
- package/types/src/vitest/forge-build.plugin.d.ts +0 -2
|
@@ -135,11 +135,11 @@ class SwcTranspiler extends (0, _extensibility.mixwith)(_cachesoutputsmixin.Cach
|
|
|
135
135
|
* Async variant of {@link transformFileSync}, with the same result for the same file. SWC
|
|
136
136
|
* runs it on libuv's thread pool, so many calls in flight compile in parallel (#661).
|
|
137
137
|
*/ async transformFileAsync(file, buildTarget, options = {}, baseConfig = this.createBaseConfig(buildTarget, options)) {
|
|
138
|
-
const swcOptions = this.#swcConfigFactory.createFileConfig(file, baseConfig);
|
|
139
138
|
let content;
|
|
140
139
|
try {
|
|
141
140
|
content = await _nodefs.default.promises.readFile(file.fullPath, "utf8");
|
|
142
141
|
} catch (error) {
|
|
142
|
+
const swcOptions = this.#swcConfigFactory.createFileConfig(file, baseConfig);
|
|
143
143
|
return _transformresponsemodel.TransformResponse.Error({
|
|
144
144
|
file,
|
|
145
145
|
messages: [
|
|
@@ -148,6 +148,14 @@ class SwcTranspiler extends (0, _extensibility.mixwith)(_cachesoutputsmixin.Cach
|
|
|
148
148
|
config: swcOptions
|
|
149
149
|
});
|
|
150
150
|
}
|
|
151
|
+
return this.transformCode(content, file, buildTarget, baseConfig);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Compiles `content` as the source of `file`, without reading the file: for callers that
|
|
155
|
+
* already hold the code, such as the Vitest plugin, which must compile what Vite passes it
|
|
156
|
+
* rather than an earlier read of the disk (#517). Async SWC, on the thread pool.
|
|
157
|
+
*/ async transformCode(content, file, buildTarget, baseConfig) {
|
|
158
|
+
const swcOptions = this.#swcConfigFactory.createFileConfig(file, baseConfig);
|
|
151
159
|
if (content === "") return SwcTranspiler.skippedEmpty(file, swcOptions);
|
|
152
160
|
try {
|
|
153
161
|
const transformed = await (0, _core.transform)(content, swcOptions);
|
|
@@ -43,9 +43,11 @@ function forgeBuild(options) {
|
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
45
|
const logger = loggerFactory(PLUGIN_NAME);
|
|
46
|
-
const skipBuild = options?.skipBuild ?? false;
|
|
47
46
|
const transpiler = new _swctranspiler.SwcTranspiler();
|
|
48
|
-
|
|
47
|
+
/**
|
|
48
|
+
* SWC options per project (tsconfig, `.swcrc`, path aliases), built once: rebuilding them for
|
|
49
|
+
* every file was most of the plugin's per-file cost, as it was in flux (#513, #517).
|
|
50
|
+
*/ const baseConfigs = new Map();
|
|
49
51
|
const transformCache = new Map();
|
|
50
52
|
let workspace = null;
|
|
51
53
|
const initWorkspace = async ()=>{
|
|
@@ -56,10 +58,17 @@ function forgeBuild(options) {
|
|
|
56
58
|
rootDir: workspaceRoot
|
|
57
59
|
});
|
|
58
60
|
};
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
const baseConfigFor = (project)=>{
|
|
62
|
+
const cached = baseConfigs.get(project);
|
|
63
|
+
if (cached != null) return cached;
|
|
64
|
+
const config = transpiler.createBaseConfig(_buildtargettype.BuildTargets.es6, {
|
|
65
|
+
aliasPaths: true,
|
|
66
|
+
tsConfig: project?.getTsConfigSync(),
|
|
67
|
+
userConfig: project?.getSwcConfigSync(),
|
|
68
|
+
workspace: workspace ?? undefined
|
|
69
|
+
});
|
|
70
|
+
baseConfigs.set(project, config);
|
|
71
|
+
return config;
|
|
63
72
|
};
|
|
64
73
|
return {
|
|
65
74
|
name: PLUGIN_NAME,
|
|
@@ -77,42 +86,24 @@ function forgeBuild(options) {
|
|
|
77
86
|
return null;
|
|
78
87
|
},
|
|
79
88
|
watchChange (id) {
|
|
80
|
-
const file = new _io.FileEx({
|
|
81
|
-
fullPath: id
|
|
82
|
-
});
|
|
83
89
|
transformCache.delete(id);
|
|
84
|
-
transpiler.invalidateContentCache(file);
|
|
85
90
|
},
|
|
86
91
|
async transform (code, id) {
|
|
87
92
|
if (id.includes("node_modules") || !/[.](ts|tsx|mts|cts)$/.test(id)) {
|
|
88
93
|
return null;
|
|
89
94
|
}
|
|
90
|
-
await initWorkspace();
|
|
91
|
-
const project = await workspace?.findProject(id, buildConfig);
|
|
92
|
-
if (project != null && !skipBuild) {
|
|
93
|
-
preWarmProject(project);
|
|
94
|
-
}
|
|
95
95
|
const cached = transformCache.get(id);
|
|
96
96
|
if (cached !== undefined && cached.content === code) {
|
|
97
97
|
return cached.result;
|
|
98
98
|
}
|
|
99
|
-
|
|
99
|
+
await initWorkspace();
|
|
100
|
+
const project = await workspace?.findProject(id, buildConfig);
|
|
101
|
+
// The code Vite passes, not a read of the file: no content cache can serve an older
|
|
102
|
+
// version, and there is no pre-warm compiling files ahead from disk (#517).
|
|
103
|
+
const response = await transpiler.transformCode(code, new _io.FileEx({
|
|
100
104
|
fullPath: id,
|
|
101
105
|
content: code
|
|
102
|
-
});
|
|
103
|
-
if (cached !== undefined) {
|
|
104
|
-
// Content changed for a previously-seen file — invalidate the transpiler's
|
|
105
|
-
// FILE_CONTENT cache so the fresh content from Vite is used.
|
|
106
|
-
// The VS Code Vitest extension disables Vite's chokidar watcher
|
|
107
|
-
// (server.watch: null) and drives re-runs via vitest.watcher.onFileChange(),
|
|
108
|
-
// bypassing watchChange entirely — so we cannot rely on watchChange alone.
|
|
109
|
-
transpiler.invalidateContentCache(file);
|
|
110
|
-
}
|
|
111
|
-
const [response] = await transpiler.transformFile(file, _buildtargettype.BuildTargets.es6, {
|
|
112
|
-
aliasPaths: true,
|
|
113
|
-
tsConfig: project ? await project.getTsConfig() : undefined,
|
|
114
|
-
workspace: workspace ?? undefined
|
|
115
|
-
});
|
|
106
|
+
}), _buildtargettype.BuildTargets.es6, baseConfigFor(project));
|
|
116
107
|
if (!response || response.outcome !== "success") return null;
|
|
117
108
|
const result = {
|
|
118
109
|
code: response.code,
|
|
@@ -120,11 +120,11 @@ export class SwcTranspiler extends mixwith(CachesOutputs, IncrementalTransform)
|
|
|
120
120
|
* Async variant of {@link transformFileSync}, with the same result for the same file. SWC
|
|
121
121
|
* runs it on libuv's thread pool, so many calls in flight compile in parallel (#661).
|
|
122
122
|
*/ async transformFileAsync(file, buildTarget, options = {}, baseConfig = this.createBaseConfig(buildTarget, options)) {
|
|
123
|
-
const swcOptions = this.#swcConfigFactory.createFileConfig(file, baseConfig);
|
|
124
123
|
let content;
|
|
125
124
|
try {
|
|
126
125
|
content = await fs.promises.readFile(file.fullPath, "utf8");
|
|
127
126
|
} catch (error) {
|
|
127
|
+
const swcOptions = this.#swcConfigFactory.createFileConfig(file, baseConfig);
|
|
128
128
|
return TransformResponse.Error({
|
|
129
129
|
file,
|
|
130
130
|
messages: [
|
|
@@ -133,6 +133,14 @@ export class SwcTranspiler extends mixwith(CachesOutputs, IncrementalTransform)
|
|
|
133
133
|
config: swcOptions
|
|
134
134
|
});
|
|
135
135
|
}
|
|
136
|
+
return this.transformCode(content, file, buildTarget, baseConfig);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Compiles `content` as the source of `file`, without reading the file: for callers that
|
|
140
|
+
* already hold the code, such as the Vitest plugin, which must compile what Vite passes it
|
|
141
|
+
* rather than an earlier read of the disk (#517). Async SWC, on the thread pool.
|
|
142
|
+
*/ async transformCode(content, file, buildTarget, baseConfig) {
|
|
143
|
+
const swcOptions = this.#swcConfigFactory.createFileConfig(file, baseConfig);
|
|
136
144
|
if (content === "") return SwcTranspiler.skippedEmpty(file, swcOptions);
|
|
137
145
|
try {
|
|
138
146
|
const transformed = await transform(content, swcOptions);
|
|
@@ -28,9 +28,11 @@ export function forgeBuild(options) {
|
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
30
|
const logger = loggerFactory(PLUGIN_NAME);
|
|
31
|
-
const skipBuild = options?.skipBuild ?? false;
|
|
32
31
|
const transpiler = new SwcTranspiler();
|
|
33
|
-
|
|
32
|
+
/**
|
|
33
|
+
* SWC options per project (tsconfig, `.swcrc`, path aliases), built once: rebuilding them for
|
|
34
|
+
* every file was most of the plugin's per-file cost, as it was in flux (#513, #517).
|
|
35
|
+
*/ const baseConfigs = new Map();
|
|
34
36
|
const transformCache = new Map();
|
|
35
37
|
let workspace = null;
|
|
36
38
|
const initWorkspace = async ()=>{
|
|
@@ -41,10 +43,17 @@ export function forgeBuild(options) {
|
|
|
41
43
|
rootDir: workspaceRoot
|
|
42
44
|
});
|
|
43
45
|
};
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
const baseConfigFor = (project)=>{
|
|
47
|
+
const cached = baseConfigs.get(project);
|
|
48
|
+
if (cached != null) return cached;
|
|
49
|
+
const config = transpiler.createBaseConfig(BuildTargets.es6, {
|
|
50
|
+
aliasPaths: true,
|
|
51
|
+
tsConfig: project?.getTsConfigSync(),
|
|
52
|
+
userConfig: project?.getSwcConfigSync(),
|
|
53
|
+
workspace: workspace ?? undefined
|
|
54
|
+
});
|
|
55
|
+
baseConfigs.set(project, config);
|
|
56
|
+
return config;
|
|
48
57
|
};
|
|
49
58
|
return {
|
|
50
59
|
name: PLUGIN_NAME,
|
|
@@ -62,42 +71,24 @@ export function forgeBuild(options) {
|
|
|
62
71
|
return null;
|
|
63
72
|
},
|
|
64
73
|
watchChange (id) {
|
|
65
|
-
const file = new FileEx({
|
|
66
|
-
fullPath: id
|
|
67
|
-
});
|
|
68
74
|
transformCache.delete(id);
|
|
69
|
-
transpiler.invalidateContentCache(file);
|
|
70
75
|
},
|
|
71
76
|
async transform (code, id) {
|
|
72
77
|
if (id.includes("node_modules") || !/[.](ts|tsx|mts|cts)$/.test(id)) {
|
|
73
78
|
return null;
|
|
74
79
|
}
|
|
75
|
-
await initWorkspace();
|
|
76
|
-
const project = await workspace?.findProject(id, buildConfig);
|
|
77
|
-
if (project != null && !skipBuild) {
|
|
78
|
-
preWarmProject(project);
|
|
79
|
-
}
|
|
80
80
|
const cached = transformCache.get(id);
|
|
81
81
|
if (cached !== undefined && cached.content === code) {
|
|
82
82
|
return cached.result;
|
|
83
83
|
}
|
|
84
|
-
|
|
84
|
+
await initWorkspace();
|
|
85
|
+
const project = await workspace?.findProject(id, buildConfig);
|
|
86
|
+
// The code Vite passes, not a read of the file: no content cache can serve an older
|
|
87
|
+
// version, and there is no pre-warm compiling files ahead from disk (#517).
|
|
88
|
+
const response = await transpiler.transformCode(code, new FileEx({
|
|
85
89
|
fullPath: id,
|
|
86
90
|
content: code
|
|
87
|
-
});
|
|
88
|
-
if (cached !== undefined) {
|
|
89
|
-
// Content changed for a previously-seen file — invalidate the transpiler's
|
|
90
|
-
// FILE_CONTENT cache so the fresh content from Vite is used.
|
|
91
|
-
// The VS Code Vitest extension disables Vite's chokidar watcher
|
|
92
|
-
// (server.watch: null) and drives re-runs via vitest.watcher.onFileChange(),
|
|
93
|
-
// bypassing watchChange entirely — so we cannot rely on watchChange alone.
|
|
94
|
-
transpiler.invalidateContentCache(file);
|
|
95
|
-
}
|
|
96
|
-
const [response] = await transpiler.transformFile(file, BuildTargets.es6, {
|
|
97
|
-
aliasPaths: true,
|
|
98
|
-
tsConfig: project ? await project.getTsConfig() : undefined,
|
|
99
|
-
workspace: workspace ?? undefined
|
|
100
|
-
});
|
|
91
|
+
}), BuildTargets.es6, baseConfigFor(project));
|
|
101
92
|
if (!response || response.outcome !== "success") return null;
|
|
102
93
|
const result = {
|
|
103
94
|
code: response.code,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decohere/forge",
|
|
3
|
-
"version": "0.0.1-dev.
|
|
3
|
+
"version": "0.0.1-dev.112",
|
|
4
4
|
"description": "TypeScript monorepo build system — zero config, fast compilation, streamlined developer workflow",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@decohere/crucible": "0.0.1-dev.
|
|
33
|
-
"@decohere/mesh": "0.0.1-dev.
|
|
34
|
-
"@decohere/nexus": "0.0.1-dev.
|
|
35
|
-
"@decohere/scribe": "0.0.1-dev.
|
|
32
|
+
"@decohere/crucible": "0.0.1-dev.112",
|
|
33
|
+
"@decohere/mesh": "0.0.1-dev.112",
|
|
34
|
+
"@decohere/nexus": "0.0.1-dev.112",
|
|
35
|
+
"@decohere/scribe": "0.0.1-dev.112",
|
|
36
36
|
"@swc/core": "^1.11.11",
|
|
37
37
|
"async": "^3.2.6",
|
|
38
38
|
"parse-imports": "^2.2.1",
|
|
@@ -42,6 +42,12 @@ export declare class SwcTranspiler extends SwcTranspiler_base implements Transfo
|
|
|
42
42
|
* runs it on libuv's thread pool, so many calls in flight compile in parallel (#661).
|
|
43
43
|
*/
|
|
44
44
|
transformFileAsync(file: FileEx, buildTarget: TranspileTarget, options?: TranspileOptions, baseConfig?: Options): Promise<TransformResponse>;
|
|
45
|
+
/**
|
|
46
|
+
* Compiles `content` as the source of `file`, without reading the file: for callers that
|
|
47
|
+
* already hold the code, such as the Vitest plugin, which must compile what Vite passes it
|
|
48
|
+
* rather than an earlier read of the disk (#517). Async SWC, on the thread pool.
|
|
49
|
+
*/
|
|
50
|
+
transformCode(content: string, file: FileEx, buildTarget: TranspileTarget, baseConfig: Options): Promise<TransformResponse>;
|
|
45
51
|
private static skippedEmpty;
|
|
46
52
|
private static succeeded;
|
|
47
53
|
private transformTarget;
|
|
@@ -2,8 +2,6 @@ export type ForgeBuildOptions = {
|
|
|
2
2
|
buildConfig?: string;
|
|
3
3
|
logLevel?: string;
|
|
4
4
|
workspaceRoot?: string;
|
|
5
|
-
/** Skip the build pre-warm entirely (e.g. when a pre-built artifact is already on disk). */
|
|
6
|
-
skipBuild?: boolean;
|
|
7
5
|
};
|
|
8
6
|
export declare function forgeBuild(options?: ForgeBuildOptions): {
|
|
9
7
|
name: string;
|