@speedkit/cli 4.24.0 → 4.24.2
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/CHANGELOG.md +15 -0
- package/README.md +1 -1
- package/dist/services/bundler/bundle-service-factory.d.ts +1 -2
- package/dist/services/bundler/bundle-service-factory.js +2 -4
- package/dist/services/bundler/bundle-service.d.ts +3 -24
- package/dist/services/bundler/bundle-service.js +4 -54
- package/dist/services/bundler/bundle-service.spec.js +23 -89
- package/dist/services/document-handler-runtime/document-handler-server.d.ts +18 -1
- package/dist/services/document-handler-runtime/document-handler-server.js +47 -2
- package/dist/services/document-handler-runtime/factory/document-handler-runtime-service-factory.d.ts +11 -0
- package/dist/services/document-handler-runtime/factory/document-handler-runtime-service-factory.js +25 -7
- package/oclif.manifest.json +1 -1
- package/package.json +11 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## [4.24.2](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.24.1...v4.24.2) (2026-09-03)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **onboarding:** match the document handler's own dependency ranges ([414bd14](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/414bd14584778c0e7b1a3f556c311cd997380df7))
|
|
7
|
+
* **onboarding:** resolve document handler modules at runtime ([44aa76e](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/44aa76ef1b7bf846e47b0be7f6e11b850d7dd0e6))
|
|
8
|
+
|
|
9
|
+
## [4.24.1](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.24.0...v4.24.1) (2026-09-02)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* **onboarding:** install config dependencies into the workspace ([20a091e](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/20a091e4b5deff21559363ec4baa960f0c350ec3))
|
|
15
|
+
|
|
1
16
|
# [4.24.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.23.3...v4.24.0) (2026-08-28)
|
|
2
17
|
|
|
3
18
|
|
package/README.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { BundleService } from "./bundle-service.js";
|
|
2
2
|
export declare class BundleServiceFactory {
|
|
3
3
|
private customerFolder?;
|
|
4
|
-
|
|
5
|
-
constructor(customerFolder?: string, additionalModulePaths?: string[]);
|
|
4
|
+
constructor(customerFolder?: string);
|
|
6
5
|
buildService(): BundleService;
|
|
7
6
|
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { BundleService } from "./bundle-service.js";
|
|
2
2
|
export class BundleServiceFactory {
|
|
3
3
|
customerFolder;
|
|
4
|
-
|
|
5
|
-
constructor(customerFolder, additionalModulePaths = []) {
|
|
4
|
+
constructor(customerFolder) {
|
|
6
5
|
this.customerFolder = customerFolder;
|
|
7
|
-
this.additionalModulePaths = additionalModulePaths;
|
|
8
6
|
}
|
|
9
7
|
buildService() {
|
|
10
|
-
return new BundleService(this.customerFolder
|
|
8
|
+
return new BundleService(this.customerFolder);
|
|
11
9
|
}
|
|
12
10
|
}
|
|
@@ -8,15 +8,11 @@ export type BundleBasePaths = Record<string, string>;
|
|
|
8
8
|
export type BundleResolveFallback = (specifier: string) => Promise<string | null>;
|
|
9
9
|
export declare class BundleService {
|
|
10
10
|
private moduleDiscoveryDir;
|
|
11
|
-
private additionalModulePaths;
|
|
12
11
|
/**
|
|
13
12
|
* @param moduleDiscoveryDir the directory bundled code resolves its own imports from
|
|
14
|
-
* @param additionalModulePaths extra `node_modules` directories to search, like `NODE_PATH`.
|
|
15
|
-
* `sk` installs the packages a customer config declares under `dependencies` into its cache
|
|
16
|
-
* rather than into the customer folder, so esbuild would otherwise never find them.
|
|
17
13
|
*/
|
|
18
|
-
constructor(moduleDiscoveryDir: string
|
|
19
|
-
bundle({ entryPoints, basePaths, define, target, minify, logLevel, alias, platform, resolveFallback, }: {
|
|
14
|
+
constructor(moduleDiscoveryDir: string);
|
|
15
|
+
bundle({ entryPoints, basePaths, define, target, minify, logLevel, alias, platform, resolveFallback, packages, }: {
|
|
20
16
|
entryPoints: BundleEntryPoints;
|
|
21
17
|
basePaths?: BundleBasePaths;
|
|
22
18
|
target?: string | string[];
|
|
@@ -26,25 +22,8 @@ export declare class BundleService {
|
|
|
26
22
|
logLevel?: LogLevel;
|
|
27
23
|
platform?: "browser" | "node" | "neutral";
|
|
28
24
|
resolveFallback?: BundleResolveFallback;
|
|
25
|
+
packages?: "bundle" | "external";
|
|
29
26
|
}): Promise<string>;
|
|
30
|
-
/**
|
|
31
|
-
* Turns each alias target into an absolute path, resolved from the same directory the bundled
|
|
32
|
-
* code resolves its own imports from.
|
|
33
|
-
*
|
|
34
|
-
* esbuild resolves an alias target from its working directory, not from the entry point's
|
|
35
|
-
* resolveDir. `{ "node-fetch": "node-fetch-native" }` therefore only resolved while `sk` ran
|
|
36
|
-
* inside the customer checkout, and failed from anywhere else — even though the bundled code
|
|
37
|
-
* could import the very same module by name.
|
|
38
|
-
*
|
|
39
|
-
* A target that is not installed stays a bare name, so esbuild reports it as before.
|
|
40
|
-
*/
|
|
41
|
-
private resolveAliases;
|
|
42
|
-
/**
|
|
43
|
-
* Looks the target up the way the bundled code would: the customer folder first, then the
|
|
44
|
-
* additional module paths. Returns `null` when it is installed nowhere, so the bare name reaches
|
|
45
|
-
* esbuild and is reported as before.
|
|
46
|
-
*/
|
|
47
|
-
private resolveAliasTarget;
|
|
48
27
|
private entryPointsPlugin;
|
|
49
28
|
baqendPlugin(basePaths: BundleBasePaths): {
|
|
50
29
|
name: string;
|
|
@@ -4,24 +4,18 @@ import { URL } from "node:url";
|
|
|
4
4
|
import { BundleResolveError } from "./error/bundle-resolve-error.js";
|
|
5
5
|
import { BundleFetchError } from "./error/bundle-fetch-error.js";
|
|
6
6
|
import { resolve } from "node:path";
|
|
7
|
-
import { createRequire } from "node:module";
|
|
8
7
|
const FALLBACK_NAMESPACE = "bundle-resolve-fallback";
|
|
9
8
|
const RELATIVE_SPECIFIER = /^\.{1,2}\//;
|
|
10
9
|
const importCache = {};
|
|
11
10
|
export class BundleService {
|
|
12
11
|
moduleDiscoveryDir;
|
|
13
|
-
additionalModulePaths;
|
|
14
12
|
/**
|
|
15
13
|
* @param moduleDiscoveryDir the directory bundled code resolves its own imports from
|
|
16
|
-
* @param additionalModulePaths extra `node_modules` directories to search, like `NODE_PATH`.
|
|
17
|
-
* `sk` installs the packages a customer config declares under `dependencies` into its cache
|
|
18
|
-
* rather than into the customer folder, so esbuild would otherwise never find them.
|
|
19
14
|
*/
|
|
20
|
-
constructor(moduleDiscoveryDir
|
|
15
|
+
constructor(moduleDiscoveryDir) {
|
|
21
16
|
this.moduleDiscoveryDir = moduleDiscoveryDir;
|
|
22
|
-
this.additionalModulePaths = additionalModulePaths;
|
|
23
17
|
}
|
|
24
|
-
async bundle({ entryPoints, basePaths, define, target = "es5", minify = true, logLevel = "silent", alias = {}, platform, resolveFallback, }) {
|
|
18
|
+
async bundle({ entryPoints, basePaths, define, target = "es5", minify = true, logLevel = "silent", alias = {}, platform, resolveFallback, packages = "bundle", }) {
|
|
25
19
|
const plugins = [];
|
|
26
20
|
if (basePaths && Object.keys(basePaths).length > 0) {
|
|
27
21
|
plugins.push(this.baqendPlugin(basePaths));
|
|
@@ -43,58 +37,14 @@ export class BundleService {
|
|
|
43
37
|
entryPoints: Object.keys(entryPoints),
|
|
44
38
|
platform: platform || "neutral",
|
|
45
39
|
logLevel,
|
|
46
|
-
|
|
47
|
-
alias
|
|
40
|
+
packages,
|
|
41
|
+
alias,
|
|
48
42
|
});
|
|
49
43
|
return result.outputFiles
|
|
50
44
|
.map((outputFile) => outputFile.text)
|
|
51
45
|
.join("")
|
|
52
46
|
.trim();
|
|
53
47
|
}
|
|
54
|
-
/**
|
|
55
|
-
* Turns each alias target into an absolute path, resolved from the same directory the bundled
|
|
56
|
-
* code resolves its own imports from.
|
|
57
|
-
*
|
|
58
|
-
* esbuild resolves an alias target from its working directory, not from the entry point's
|
|
59
|
-
* resolveDir. `{ "node-fetch": "node-fetch-native" }` therefore only resolved while `sk` ran
|
|
60
|
-
* inside the customer checkout, and failed from anywhere else — even though the bundled code
|
|
61
|
-
* could import the very same module by name.
|
|
62
|
-
*
|
|
63
|
-
* A target that is not installed stays a bare name, so esbuild reports it as before.
|
|
64
|
-
*/
|
|
65
|
-
resolveAliases(alias) {
|
|
66
|
-
if (Object.keys(alias).length === 0) {
|
|
67
|
-
return alias;
|
|
68
|
-
}
|
|
69
|
-
// The filename never has to exist; only its directory decides where the lookup starts.
|
|
70
|
-
const requireFrom = createRequire(resolve(this.moduleDiscoveryDir, "noop.js"));
|
|
71
|
-
return Object.fromEntries(Object.entries(alias).map(([from, to]) => [
|
|
72
|
-
from,
|
|
73
|
-
this.resolveAliasTarget(requireFrom, to) ?? to,
|
|
74
|
-
]));
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Looks the target up the way the bundled code would: the customer folder first, then the
|
|
78
|
-
* additional module paths. Returns `null` when it is installed nowhere, so the bare name reaches
|
|
79
|
-
* esbuild and is reported as before.
|
|
80
|
-
*/
|
|
81
|
-
resolveAliasTarget(requireFrom, target) {
|
|
82
|
-
try {
|
|
83
|
-
return requireFrom.resolve(target);
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
// not in the customer folder — fall through to the additional module paths
|
|
87
|
-
}
|
|
88
|
-
for (const modulePath of this.additionalModulePaths) {
|
|
89
|
-
try {
|
|
90
|
-
return createRequire(resolve(modulePath, "noop.js")).resolve(target);
|
|
91
|
-
}
|
|
92
|
-
catch {
|
|
93
|
-
// keep looking
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
48
|
entryPointsPlugin(entryPoints) {
|
|
99
49
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
100
50
|
const me = this;
|
|
@@ -1,62 +1,9 @@
|
|
|
1
1
|
import { expect } from "chai";
|
|
2
2
|
import { after, before, describe, it } from "mocha";
|
|
3
|
-
import {
|
|
3
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { BundleService } from "./bundle-service.js";
|
|
7
|
-
/**
|
|
8
|
-
* The customer checkout installs the modules a document handler imports, so resolution must follow
|
|
9
|
-
* the customer directory rather than the directory `sk` happens to run in. The fixture below is
|
|
10
|
-
* only reachable from the customer directory, which is what makes the assertion meaningful.
|
|
11
|
-
*/
|
|
12
|
-
describe("BundleService alias resolution", () => {
|
|
13
|
-
const MARKER = "fixture-fetch-implementation";
|
|
14
|
-
let customerDirectory;
|
|
15
|
-
before(() => {
|
|
16
|
-
customerDirectory = mkdtempSync(join(tmpdir(), "sk-bundle-"));
|
|
17
|
-
const packageDirectory = join(customerDirectory, "node_modules", "fetch-native-fixture");
|
|
18
|
-
mkdirSync(packageDirectory, { recursive: true });
|
|
19
|
-
writeFileSync(join(packageDirectory, "package.json"), JSON.stringify({
|
|
20
|
-
name: "fetch-native-fixture",
|
|
21
|
-
version: "1.0.0",
|
|
22
|
-
main: "index.js",
|
|
23
|
-
}));
|
|
24
|
-
writeFileSync(join(packageDirectory, "index.js"), `module.exports = "${MARKER}";`);
|
|
25
|
-
});
|
|
26
|
-
after(() => {
|
|
27
|
-
rmSync(customerDirectory, { recursive: true, force: true });
|
|
28
|
-
});
|
|
29
|
-
it("resolves an alias target from the customer directory, not from the working directory", async () => {
|
|
30
|
-
const bundle = await new BundleService(customerDirectory).bundle({
|
|
31
|
-
entryPoints: {
|
|
32
|
-
"handler.js": "module.exports = require('node-fetch');",
|
|
33
|
-
},
|
|
34
|
-
minify: false,
|
|
35
|
-
platform: "node",
|
|
36
|
-
logLevel: "silent",
|
|
37
|
-
alias: { "node-fetch": "fetch-native-fixture" },
|
|
38
|
-
});
|
|
39
|
-
expect(bundle).to.contain(MARKER);
|
|
40
|
-
});
|
|
41
|
-
it("leaves an uninstalled alias target to esbuild", async () => {
|
|
42
|
-
const bundling = new BundleService(customerDirectory).bundle({
|
|
43
|
-
entryPoints: {
|
|
44
|
-
"handler.js": "module.exports = require('node-fetch');",
|
|
45
|
-
},
|
|
46
|
-
minify: false,
|
|
47
|
-
platform: "node",
|
|
48
|
-
logLevel: "silent",
|
|
49
|
-
alias: { "node-fetch": "not-installed-anywhere" },
|
|
50
|
-
});
|
|
51
|
-
const error = await bundling.then(() => undefined, (reason) => reason);
|
|
52
|
-
expect(String(error)).to.contain("not-installed-anywhere");
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
/**
|
|
56
|
-
* A document handler is shared with the Baqend app it is deployed to, so it can require a module
|
|
57
|
-
* that only exists there. The bundler must reach for that copy only after the customer folder has
|
|
58
|
-
* had its chance, and only for the relative specifiers Baqend modules are required by.
|
|
59
|
-
*/
|
|
60
7
|
describe("BundleService resolve fallback", () => {
|
|
61
8
|
const bundleWithFallback = (source, fallback, moduleDiscoveryDir = process.cwd()) => new BundleService(moduleDiscoveryDir).bundle({
|
|
62
9
|
entryPoints: { "handler.js": source },
|
|
@@ -111,68 +58,55 @@ describe("BundleService resolve fallback", () => {
|
|
|
111
58
|
});
|
|
112
59
|
});
|
|
113
60
|
/**
|
|
114
|
-
*
|
|
115
|
-
*
|
|
61
|
+
* A document handler imports packages that must not be bundled — a native addon locates its own
|
|
62
|
+
* binary through `import.meta.url`, which an esbuild CJS bundle leaves undefined. Only bare
|
|
63
|
+
* specifiers become runtime requires; the customer's own files still have to be bundled.
|
|
116
64
|
*/
|
|
117
|
-
describe("BundleService
|
|
118
|
-
const MARKER = "fixture-
|
|
119
|
-
let cacheDirectory;
|
|
65
|
+
describe("BundleService external packages", () => {
|
|
66
|
+
const MARKER = "fixture-relative-module";
|
|
120
67
|
let customerDirectory;
|
|
121
|
-
let additionalModulePath;
|
|
122
68
|
before(() => {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
additionalModulePath = join(cacheDirectory, "node_modules");
|
|
126
|
-
const packageDirectory = join(additionalModulePath, "additional-fixture");
|
|
127
|
-
mkdirSync(packageDirectory, { recursive: true });
|
|
128
|
-
writeFileSync(join(packageDirectory, "package.json"), JSON.stringify({
|
|
129
|
-
name: "additional-fixture",
|
|
130
|
-
version: "1.0.0",
|
|
131
|
-
main: "index.js",
|
|
132
|
-
}));
|
|
133
|
-
writeFileSync(join(packageDirectory, "index.js"), `module.exports = "${MARKER}";`);
|
|
69
|
+
customerDirectory = mkdtempSync(join(tmpdir(), "sk-external-"));
|
|
70
|
+
writeFileSync(join(customerDirectory, "helper.js"), `module.exports = "${MARKER}";`);
|
|
134
71
|
});
|
|
135
72
|
after(() => {
|
|
136
|
-
rmSync(cacheDirectory, { recursive: true, force: true });
|
|
137
73
|
rmSync(customerDirectory, { recursive: true, force: true });
|
|
138
74
|
});
|
|
139
|
-
it("
|
|
140
|
-
const bundle = await new BundleService(customerDirectory
|
|
141
|
-
additionalModulePath,
|
|
142
|
-
]).bundle({
|
|
75
|
+
it("leaves a package as a runtime require", async () => {
|
|
76
|
+
const bundle = await new BundleService(customerDirectory).bundle({
|
|
143
77
|
entryPoints: {
|
|
144
|
-
"handler.js": "module.exports = require('
|
|
78
|
+
"handler.js": "module.exports = require('parse5');",
|
|
145
79
|
},
|
|
146
80
|
minify: false,
|
|
147
81
|
platform: "node",
|
|
148
82
|
logLevel: "silent",
|
|
83
|
+
packages: "external",
|
|
149
84
|
});
|
|
150
|
-
expect(bundle).to.contain(
|
|
85
|
+
expect(bundle).to.contain('require("parse5")');
|
|
151
86
|
});
|
|
152
|
-
it("
|
|
153
|
-
const
|
|
87
|
+
it("still bundles a relative import", async () => {
|
|
88
|
+
const bundle = await new BundleService(customerDirectory).bundle({
|
|
154
89
|
entryPoints: {
|
|
155
|
-
"handler.js": "module.exports = require('
|
|
90
|
+
"handler.js": "module.exports = require('./helper.js');",
|
|
156
91
|
},
|
|
157
92
|
minify: false,
|
|
158
93
|
platform: "node",
|
|
159
94
|
logLevel: "silent",
|
|
95
|
+
packages: "external",
|
|
160
96
|
});
|
|
161
|
-
|
|
162
|
-
expect(String(error)).to.contain("additional-fixture");
|
|
97
|
+
expect(bundle).to.contain(MARKER);
|
|
163
98
|
});
|
|
164
|
-
it("
|
|
165
|
-
const bundle = await new BundleService(customerDirectory
|
|
166
|
-
additionalModulePath,
|
|
167
|
-
]).bundle({
|
|
99
|
+
it("applies an alias and keeps the target a bare specifier", async () => {
|
|
100
|
+
const bundle = await new BundleService(customerDirectory).bundle({
|
|
168
101
|
entryPoints: {
|
|
169
102
|
"handler.js": "module.exports = require('node-fetch');",
|
|
170
103
|
},
|
|
171
104
|
minify: false,
|
|
172
105
|
platform: "node",
|
|
173
106
|
logLevel: "silent",
|
|
174
|
-
alias: { "node-fetch": "
|
|
107
|
+
alias: { "node-fetch": "node-fetch-native" },
|
|
108
|
+
packages: "external",
|
|
175
109
|
});
|
|
176
|
-
expect(bundle).to.contain(
|
|
110
|
+
expect(bundle).to.contain('require("node-fetch-native")');
|
|
177
111
|
});
|
|
178
112
|
});
|
|
@@ -8,6 +8,7 @@ export declare class DocumentHandlerServer {
|
|
|
8
8
|
private files;
|
|
9
9
|
private bundler;
|
|
10
10
|
private cli;
|
|
11
|
+
private workSpace;
|
|
11
12
|
private verboseLevel;
|
|
12
13
|
private entityManager?;
|
|
13
14
|
private documentHandlerCode;
|
|
@@ -15,7 +16,11 @@ export declare class DocumentHandlerServer {
|
|
|
15
16
|
private database;
|
|
16
17
|
/** Sources of app modules already looked up; `null` marks a lookup that came back empty. */
|
|
17
18
|
private readonly appModules;
|
|
18
|
-
|
|
19
|
+
private moduleResolver;
|
|
20
|
+
/**
|
|
21
|
+
* @param workSpace - Directory the packages a customer config declares are installed into.
|
|
22
|
+
*/
|
|
23
|
+
constructor(customerConfig: CustomerConfig, files: FileListInterface, bundler: BundleService, cli: CliService, workSpace: string, verboseLevel?: boolean, entityManager?: EntityManager);
|
|
19
24
|
transform(contentType: string, html: string, variation: string, url: string, headers: Record<string, string>): Promise<DocumentHandlerResponse>;
|
|
20
25
|
private getTransformFunctionWrapper;
|
|
21
26
|
buildDocumentHandler(): Promise<void>;
|
|
@@ -43,6 +48,18 @@ export declare class DocumentHandlerServer {
|
|
|
43
48
|
*/
|
|
44
49
|
bundleDynamicFetcher(basePaths: Record<string, string>, dynamicFetcher: string): Promise<string>;
|
|
45
50
|
bundleDocumentHandlerCode(name: string, code: string): Promise<string>;
|
|
51
|
+
/**
|
|
52
|
+
* Resolves the packages the bundle leaves as runtime requires.
|
|
53
|
+
*
|
|
54
|
+
* Nothing a document handler imports by name is bundled, because a bundler cannot serve them
|
|
55
|
+
* all: a native addon such as `node-av` locates its own `.node` file through `import.meta.url`,
|
|
56
|
+
* which an esbuild CJS bundle leaves undefined. Requiring at runtime also matches production,
|
|
57
|
+
* where the handler runs against real `node_modules`.
|
|
58
|
+
*
|
|
59
|
+
* The workspace is searched before the CLI's own dependencies, so a config that declares a
|
|
60
|
+
* package under `dependencies` also decides its version.
|
|
61
|
+
*/
|
|
62
|
+
private getModuleResolver;
|
|
46
63
|
/**
|
|
47
64
|
* Loads a module the customer folder does not contain from the app the config is deployed to.
|
|
48
65
|
*
|
|
@@ -12,12 +12,14 @@ import { safe } from "../../helpers/safe.js";
|
|
|
12
12
|
import { VmEmptyResponseError } from "../onboarding/error/vm-empty-response-error.js";
|
|
13
13
|
import { DocumentHandlerTransformError } from "../onboarding/error/document-handler-transform-error.js";
|
|
14
14
|
import { createRequire } from "node:module";
|
|
15
|
+
import { resolve } from "node:path";
|
|
15
16
|
import ApplicationError from "../error-handling/error/application-error.js";
|
|
16
17
|
export class DocumentHandlerServer {
|
|
17
18
|
customerConfig;
|
|
18
19
|
files;
|
|
19
20
|
bundler;
|
|
20
21
|
cli;
|
|
22
|
+
workSpace;
|
|
21
23
|
verboseLevel;
|
|
22
24
|
entityManager;
|
|
23
25
|
documentHandlerCode;
|
|
@@ -25,11 +27,16 @@ export class DocumentHandlerServer {
|
|
|
25
27
|
database;
|
|
26
28
|
/** Sources of app modules already looked up; `null` marks a lookup that came back empty. */
|
|
27
29
|
appModules = new Map();
|
|
28
|
-
|
|
30
|
+
moduleResolver;
|
|
31
|
+
/**
|
|
32
|
+
* @param workSpace - Directory the packages a customer config declares are installed into.
|
|
33
|
+
*/
|
|
34
|
+
constructor(customerConfig, files, bundler, cli, workSpace, verboseLevel = false, entityManager) {
|
|
29
35
|
this.customerConfig = customerConfig;
|
|
30
36
|
this.files = files;
|
|
31
37
|
this.bundler = bundler;
|
|
32
38
|
this.cli = cli;
|
|
39
|
+
this.workSpace = workSpace;
|
|
33
40
|
this.verboseLevel = verboseLevel;
|
|
34
41
|
this.entityManager = entityManager;
|
|
35
42
|
}
|
|
@@ -172,7 +179,7 @@ export class DocumentHandlerServer {
|
|
|
172
179
|
createNodeVmContext() {
|
|
173
180
|
const context = {
|
|
174
181
|
process: this.createProcessPartial(),
|
|
175
|
-
require:
|
|
182
|
+
require: this.getModuleResolver(),
|
|
176
183
|
URL,
|
|
177
184
|
URLSearchParams,
|
|
178
185
|
fetch: this.createFetchMock(),
|
|
@@ -248,6 +255,8 @@ export class DocumentHandlerServer {
|
|
|
248
255
|
platform: "node",
|
|
249
256
|
logLevel: "silent",
|
|
250
257
|
alias: { "node-fetch": "node-fetch-native" },
|
|
258
|
+
// Every package stays a runtime require, see getModuleResolver().
|
|
259
|
+
packages: "external",
|
|
251
260
|
resolveFallback: this.loadAppModule,
|
|
252
261
|
}));
|
|
253
262
|
if (bundling.success === true) {
|
|
@@ -255,6 +264,42 @@ export class DocumentHandlerServer {
|
|
|
255
264
|
}
|
|
256
265
|
throw this.explainUnresolvedModules(bundling.errorObj ?? new Error(bundling.error));
|
|
257
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Resolves the packages the bundle leaves as runtime requires.
|
|
269
|
+
*
|
|
270
|
+
* Nothing a document handler imports by name is bundled, because a bundler cannot serve them
|
|
271
|
+
* all: a native addon such as `node-av` locates its own `.node` file through `import.meta.url`,
|
|
272
|
+
* which an esbuild CJS bundle leaves undefined. Requiring at runtime also matches production,
|
|
273
|
+
* where the handler runs against real `node_modules`.
|
|
274
|
+
*
|
|
275
|
+
* The workspace is searched before the CLI's own dependencies, so a config that declares a
|
|
276
|
+
* package under `dependencies` also decides its version.
|
|
277
|
+
*/
|
|
278
|
+
getModuleResolver() {
|
|
279
|
+
if (this.moduleResolver) {
|
|
280
|
+
return this.moduleResolver;
|
|
281
|
+
}
|
|
282
|
+
// The filename never has to exist; only its directory decides where the lookup starts.
|
|
283
|
+
const requireFromWorkSpace = createRequire(resolve(this.workSpace, "noop.js"));
|
|
284
|
+
const requireFromCli = createRequire(import.meta.url);
|
|
285
|
+
// Resolve before requiring, so a module that exists but throws while it initialises
|
|
286
|
+
// reports that failure rather than falling through to the next candidate.
|
|
287
|
+
this.moduleResolver = (module) => {
|
|
288
|
+
const inWorkSpace = safe(() => requireFromWorkSpace.resolve(module));
|
|
289
|
+
if (inWorkSpace.success === true) {
|
|
290
|
+
return requireFromWorkSpace(inWorkSpace.data);
|
|
291
|
+
}
|
|
292
|
+
const withCli = safe(() => requireFromCli.resolve(module));
|
|
293
|
+
if (withCli.success === true) {
|
|
294
|
+
return requireFromCli(withCli.data);
|
|
295
|
+
}
|
|
296
|
+
throw new ApplicationError(`[documentHandler] could not load module: ${module}`, [
|
|
297
|
+
`"${module}" ships neither with the CLI nor with this customer config.`,
|
|
298
|
+
`Add it to config_customer.json under "dependencies" and start sk again.`,
|
|
299
|
+
]);
|
|
300
|
+
};
|
|
301
|
+
return this.moduleResolver;
|
|
302
|
+
}
|
|
258
303
|
/**
|
|
259
304
|
* Loads a module the customer folder does not contain from the app the config is deployed to.
|
|
260
305
|
*
|
package/dist/services/document-handler-runtime/factory/document-handler-runtime-service-factory.d.ts
CHANGED
|
@@ -18,5 +18,16 @@ export default class DocumentHandlerRuntimeServiceFactory {
|
|
|
18
18
|
private preparePackageJson;
|
|
19
19
|
private prepareTestCases;
|
|
20
20
|
private getCustomerConfig;
|
|
21
|
+
/**
|
|
22
|
+
* Gives the workspace its own manifest before anything is installed into it.
|
|
23
|
+
*
|
|
24
|
+
* npm resolves the target package by walking up from its working directory, so an
|
|
25
|
+
* empty workspace installs into the nearest ancestor package instead — on a developer
|
|
26
|
+
* machine that is often the home directory. The module then resolves only by accident,
|
|
27
|
+
* through node's upward lookup, and not at all on a machine without that ancestor.
|
|
28
|
+
*
|
|
29
|
+
* @param workSpace - Directory the additional dependencies are installed into.
|
|
30
|
+
*/
|
|
31
|
+
private prepareWorkSpaceManifest;
|
|
21
32
|
private installAdditionalDependencies;
|
|
22
33
|
}
|
package/dist/services/document-handler-runtime/factory/document-handler-runtime-service-factory.js
CHANGED
|
@@ -7,7 +7,7 @@ import { DocumentHandlerServer } from "../document-handler-server.js";
|
|
|
7
7
|
import { BundleServiceFactory } from "../../bundler/index.js";
|
|
8
8
|
import { EntityManagerFactory } from "../../config-api/entity-manager-factory.js";
|
|
9
9
|
import { safe } from "../../../helpers/safe.js";
|
|
10
|
-
import { existsSync, mkdirSync } from "node:fs";
|
|
10
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
11
11
|
import { resolve } from "node:path";
|
|
12
12
|
export default class DocumentHandlerRuntimeServiceFactory {
|
|
13
13
|
context;
|
|
@@ -36,13 +36,9 @@ export default class DocumentHandlerRuntimeServiceFactory {
|
|
|
36
36
|
const customerConfig = this.getCustomerConfig(files);
|
|
37
37
|
const cliService = new CliServiceFactory().getService();
|
|
38
38
|
await this.installAdditionalDependencies(customerConfig, cliService);
|
|
39
|
-
|
|
40
|
-
// the bundler has to search there too or a declared dependency resolves nowhere.
|
|
41
|
-
const bundler = new BundleServiceFactory(this.context.customerPath, [
|
|
42
|
-
resolve(this.cliConfig.nodeModulesPath, "node_modules"),
|
|
43
|
-
]).buildService();
|
|
39
|
+
const bundler = new BundleServiceFactory(this.context.customerPath).buildService();
|
|
44
40
|
const entityManagerResult = await safe(new EntityManagerFactory().getEntityManager(customerConfig.app));
|
|
45
|
-
return new DocumentHandlerServer(customerConfig, files, bundler, cliService, this.context.verboseLevel, entityManagerResult.success ? entityManagerResult.data : null);
|
|
41
|
+
return new DocumentHandlerServer(customerConfig, files, bundler, cliService, this.cliConfig.nodeModulesPath, this.context.verboseLevel, entityManagerResult.success ? entityManagerResult.data : null);
|
|
46
42
|
}
|
|
47
43
|
prepareIntegrationApi() {
|
|
48
44
|
const integrationApiContext = new IntegrationApiContext(this.context.customerPath, this.context.configName, this.context.fileDependencies, [".git", ".idea", "node_modules"]);
|
|
@@ -104,6 +100,27 @@ export default class DocumentHandlerRuntimeServiceFactory {
|
|
|
104
100
|
const configFile = files.getByName("config_customer");
|
|
105
101
|
return configFile.config;
|
|
106
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Gives the workspace its own manifest before anything is installed into it.
|
|
105
|
+
*
|
|
106
|
+
* npm resolves the target package by walking up from its working directory, so an
|
|
107
|
+
* empty workspace installs into the nearest ancestor package instead — on a developer
|
|
108
|
+
* machine that is often the home directory. The module then resolves only by accident,
|
|
109
|
+
* through node's upward lookup, and not at all on a machine without that ancestor.
|
|
110
|
+
*
|
|
111
|
+
* @param workSpace - Directory the additional dependencies are installed into.
|
|
112
|
+
*/
|
|
113
|
+
prepareWorkSpaceManifest(workSpace) {
|
|
114
|
+
const manifest = resolve(workSpace, "package.json");
|
|
115
|
+
if (existsSync(manifest)) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
writeFileSync(manifest, `${JSON.stringify({
|
|
119
|
+
name: "speed-kit-additional-dependencies",
|
|
120
|
+
version: "0.0.0",
|
|
121
|
+
private: true,
|
|
122
|
+
}, undefined, 2)}\n`);
|
|
123
|
+
}
|
|
107
124
|
async installAdditionalDependencies(customerConfig, cli) {
|
|
108
125
|
const workSpace = this.cliConfig.nodeModulesPath;
|
|
109
126
|
if (!customerConfig.dependencies ||
|
|
@@ -114,6 +131,7 @@ export default class DocumentHandlerRuntimeServiceFactory {
|
|
|
114
131
|
if (!existsSync(workSpace)) {
|
|
115
132
|
mkdirSync(workSpace, { recursive: true });
|
|
116
133
|
}
|
|
134
|
+
this.prepareWorkSpaceManifest(workSpace);
|
|
117
135
|
cli.write("install additional dependencies");
|
|
118
136
|
for (const packageName in customerConfig.dependencies) {
|
|
119
137
|
const version = customerConfig.dependencies[packageName];
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@speedkit/cli",
|
|
3
3
|
"description": "Speed Kit CLI",
|
|
4
|
-
"version": "4.24.
|
|
4
|
+
"version": "4.24.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Baqend.com",
|
|
7
7
|
"email": "info@baqend.com"
|
|
@@ -91,6 +91,8 @@
|
|
|
91
91
|
"@eslint/eslintrc": "^3.3.5",
|
|
92
92
|
"@eslint/js": "^10.0.1",
|
|
93
93
|
"@inquirer/prompts": "^8.4.2",
|
|
94
|
+
"@nats-io/nats-core": "^3.2.0",
|
|
95
|
+
"@nats-io/transport-node": "^3.2.0",
|
|
94
96
|
"@oclif/core": "^4.10.6",
|
|
95
97
|
"@oclif/errors": "^1.3.6",
|
|
96
98
|
"@oclif/plugin-autocomplete": "^3.2.46",
|
|
@@ -102,8 +104,13 @@
|
|
|
102
104
|
"chokidar": "^5.0.0",
|
|
103
105
|
"cli-progress": "^3.12.0",
|
|
104
106
|
"clipboardy": "^5.3.1",
|
|
107
|
+
"css-select": "^5.1.0",
|
|
105
108
|
"deepmerge": "^4.3.1",
|
|
106
109
|
"diff": "^9.0.0",
|
|
110
|
+
"dom-serializer": "^2.0.0",
|
|
111
|
+
"domelementtype": "^2.3.0",
|
|
112
|
+
"domhandler": "^5.0.3",
|
|
113
|
+
"domutils": "^3.1.0",
|
|
107
114
|
"dotenv": "^17.4.2",
|
|
108
115
|
"encoding-japanese": "^2.2.0",
|
|
109
116
|
"esbuild": "^0.27.3",
|
|
@@ -115,8 +122,11 @@
|
|
|
115
122
|
"iconv-lite": "^0.7.2",
|
|
116
123
|
"json2csv": "^6.0.0-alpha.2",
|
|
117
124
|
"node-fetch": "^3.3.2",
|
|
125
|
+
"node-fetch-native": "^1.6.7",
|
|
118
126
|
"nunjucks": "^3.2.4",
|
|
119
127
|
"nypm": "^0.6.6",
|
|
128
|
+
"parse5": "^7.1.2",
|
|
129
|
+
"parse5-htmlparser2-tree-adapter": "^7.0.0",
|
|
120
130
|
"puppeteer": "^24.41.0",
|
|
121
131
|
"puppeteer-extra": "^3.3.6",
|
|
122
132
|
"semver": "^7.7.4",
|