@hublo/sentinel 1.2.0-alpha.4 → 1.2.0-alpha.40
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/dist/bin/sentinel.d.ts +1 -0
- package/dist/bin/sentinel.js +32 -3
- package/dist/chunk-3TDUIKVQ.js +178 -0
- package/dist/chunk-NKFGYPIQ.js +13 -0
- package/dist/{chunk-6DBTDMSN.js → chunk-PBCVWRXM.js} +4657 -2960
- package/dist/index.js +1 -1
- package/dist/roles/build/nest/toolchain.d.ts +98 -0
- package/dist/roles/build/nest/toolchain.js +235 -0
- package/dist/roles/test/nest/toolchain.d.ts +17 -0
- package/dist/roles/test/nest/toolchain.js +116 -0
- package/dist/roles/test/react/toolchain.d.ts +2 -0
- package/dist/roles/test/react/toolchain.js +9 -0
- package/dist/roles/test/setup/msw-server.d.ts +15 -0
- package/dist/roles/test/setup/msw-server.js +7 -0
- package/dist/roles/test/setup/nest.d.ts +2 -0
- package/dist/roles/test/setup/nest.js +47 -0
- package/dist/tsconfig-aliases-Ce6axdJ4.d.ts +36 -0
- package/lint/react-lib.json +3 -1
- package/lint/react.json +4 -1
- package/package.json +41 -5
- package/plugins/hublo.js +68 -0
package/dist/bin/sentinel.d.ts
CHANGED
package/dist/bin/sentinel.js
CHANGED
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
VERBS,
|
|
9
9
|
WORKSPACE_ROOT_MARKER,
|
|
10
10
|
availableTargets,
|
|
11
|
+
buildConfigFile,
|
|
12
|
+
contradictingPreset,
|
|
11
13
|
declaredPresetFor,
|
|
12
14
|
describeFramework,
|
|
13
15
|
dispatch,
|
|
@@ -21,7 +23,7 @@ import {
|
|
|
21
23
|
registerAdapters,
|
|
22
24
|
resolve,
|
|
23
25
|
resolveBin
|
|
24
|
-
} from "../chunk-
|
|
26
|
+
} from "../chunk-PBCVWRXM.js";
|
|
25
27
|
|
|
26
28
|
// bin/sentinel.ts
|
|
27
29
|
import { program } from "commander";
|
|
@@ -113,6 +115,30 @@ function resolveContext(cwd2, opts2) {
|
|
|
113
115
|
);
|
|
114
116
|
}
|
|
115
117
|
|
|
118
|
+
// src/roles/build/declined.ts
|
|
119
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
120
|
+
import { join as join3 } from "path";
|
|
121
|
+
var DEPRECATED_NX_BUILDER = "@nx/webpack:webpack";
|
|
122
|
+
function buildsWithDeprecatedExecutor(cwd2) {
|
|
123
|
+
const projectJson = join3(cwd2, "project.json");
|
|
124
|
+
if (!existsSync2(projectJson)) return false;
|
|
125
|
+
try {
|
|
126
|
+
const project = JSON.parse(readFileSync2(projectJson, "utf8"));
|
|
127
|
+
return project.targets?.build?.executor === DEPRECATED_NX_BUILDER;
|
|
128
|
+
} catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function explainDecline(cwd2) {
|
|
133
|
+
if (buildConfigFile(cwd2) !== void 0) {
|
|
134
|
+
return ` This module HAS a Vite config, so detection is the likely problem: the build toolchain is declared at the workspace root, which makes a front app detect as "node". Pass --preset to say what it is.`;
|
|
135
|
+
}
|
|
136
|
+
if (buildsWithDeprecatedExecutor(cwd2)) {
|
|
137
|
+
return ` This module still builds through ${DEPRECATED_NX_BUILDER}, which Nx v24 removes. It has no Vite config yet, so there is nothing for --build to adopt: add one that imports @hublo/sentinel/build/nest, then run this again. See docs/build-adoption.md.`;
|
|
138
|
+
}
|
|
139
|
+
return ` This module has neither a Vite config nor an Nx build target, so it builds nothing and --build is out of scope for it. Nothing was written, and nothing is wrong.`;
|
|
140
|
+
}
|
|
141
|
+
|
|
116
142
|
// src/cli/run-init.ts
|
|
117
143
|
function asMessage(error) {
|
|
118
144
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -151,9 +177,10 @@ async function runInit(ctx) {
|
|
|
151
177
|
} catch (error) {
|
|
152
178
|
if (error instanceof PresetUnsupportedError) {
|
|
153
179
|
if (ctx.targetsExplicit) {
|
|
180
|
+
const hint = type === "build" ? explainDecline(module.root) : ` Pass --preset if it was detected wrongly (hoisted dependencies make detection fall back to "node").`;
|
|
154
181
|
process.stderr.write(
|
|
155
182
|
`
|
|
156
|
-
sentinel (${type}): this module's preset is "${error.preset}", and --${type} has no adapter for it
|
|
183
|
+
sentinel (${type}): this module's preset is "${error.preset}", and --${type} has no adapter for it.${hint}
|
|
157
184
|
`
|
|
158
185
|
);
|
|
159
186
|
worst = Math.max(worst, 1);
|
|
@@ -212,7 +239,9 @@ async function analyse(params) {
|
|
|
212
239
|
}
|
|
213
240
|
for (const target of params.targets) {
|
|
214
241
|
let adapter;
|
|
215
|
-
const
|
|
242
|
+
const declared = declaredPresetFor(target, module.root);
|
|
243
|
+
const corrected = params.preset ?? declared ?? contradictingPreset(module.root, preset2)?.preset ?? preset2;
|
|
244
|
+
const resolutionPreset = corrected;
|
|
216
245
|
try {
|
|
217
246
|
adapter = resolve(target, resolutionPreset, params.runner);
|
|
218
247
|
} catch (error) {
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// src/roles/build/nest/decorator-metadata.ts
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { createRequire } from "module";
|
|
4
|
+
import path from "path";
|
|
5
|
+
var TYPESCRIPT_SOURCE = /\.ts$/;
|
|
6
|
+
var TSCONFIG_CANDIDATES = ["tsconfig.app.json", "tsconfig.json"];
|
|
7
|
+
function loadCompiler(root) {
|
|
8
|
+
const require2 = createRequire(path.join(root, "noop.js"));
|
|
9
|
+
let compiler;
|
|
10
|
+
try {
|
|
11
|
+
compiler = require2("typescript");
|
|
12
|
+
} catch {
|
|
13
|
+
throw new Error(
|
|
14
|
+
`sentinel build(nest): no \`typescript\` resolvable from ${root}. The build emits decorator metadata with the module's own compiler, so one has to be installed there.`
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
if (typeof compiler.createProgram !== "function") {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`sentinel build(nest): the typescript resolved from ${root} (${compiler.version ?? "unknown"}) has no createProgram API. TypeScript 7 is the native port and exposes none; the emit API lives under @typescript/typescript6. Point this module's \`typescript\` at a compiler with an emit API, which is the one its \`typecheck\` target already uses.`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
return compiler;
|
|
23
|
+
}
|
|
24
|
+
function resolveTsconfig(options) {
|
|
25
|
+
if (options.tsconfig) {
|
|
26
|
+
const explicit = path.resolve(options.root, options.tsconfig);
|
|
27
|
+
if (!existsSync(explicit)) {
|
|
28
|
+
throw new Error(`sentinel build(nest): tsconfig not found at ${explicit}`);
|
|
29
|
+
}
|
|
30
|
+
return explicit;
|
|
31
|
+
}
|
|
32
|
+
for (const candidate of TSCONFIG_CANDIDATES) {
|
|
33
|
+
const found = path.join(options.root, candidate);
|
|
34
|
+
if (existsSync(found)) return found;
|
|
35
|
+
}
|
|
36
|
+
throw new Error(
|
|
37
|
+
`sentinel build(nest): no ${TSCONFIG_CANDIDATES.join(" or ")} in ${options.root}. Pass \`tsconfig\` if this service keeps it elsewhere.`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
function readConfig(ts, options) {
|
|
41
|
+
const configPath = resolveTsconfig(options);
|
|
42
|
+
const parsed = ts.getParsedCommandLineOfConfigFile(configPath, {}, {
|
|
43
|
+
...ts.sys,
|
|
44
|
+
onUnRecoverableConfigFileDiagnostic: (diagnostic) => {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`sentinel build(nest): could not read ${configPath}: ` + ts.flattenDiagnosticMessageText(diagnostic.messageText, " ")
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
fileNames: parsed?.fileNames ?? [],
|
|
52
|
+
compilerOptions: {
|
|
53
|
+
...parsed?.options,
|
|
54
|
+
// ESM out, so Rollup sees imports and exports rather than an opaque `require` it cannot
|
|
55
|
+
// follow. The service still SHIPS as CJS: that conversion is the bundler's, further down.
|
|
56
|
+
module: ts.ModuleKind.ESNext,
|
|
57
|
+
// Vite consumes the map; the tsconfig's own answer is about a different pipeline.
|
|
58
|
+
sourceMap: true,
|
|
59
|
+
inlineSourceMap: false,
|
|
60
|
+
inlineSources: false,
|
|
61
|
+
// Types are another target's job, and emitting them here would write into the source tree.
|
|
62
|
+
declaration: false,
|
|
63
|
+
declarationMap: false,
|
|
64
|
+
emitDeclarationOnly: false,
|
|
65
|
+
noEmit: false,
|
|
66
|
+
// `composite` projects refuse to emit without a `tsBuildInfoFile`, and there is no
|
|
67
|
+
// incremental build here to inform.
|
|
68
|
+
composite: false,
|
|
69
|
+
incremental: false
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function loadTransformers(root, program, declared) {
|
|
74
|
+
const require2 = createRequire(path.join(root, "noop.js"));
|
|
75
|
+
return declared.map(({ name, options }) => {
|
|
76
|
+
let loaded;
|
|
77
|
+
try {
|
|
78
|
+
loaded = require2(name);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`sentinel build(nest): the build target declares the transformer \`${name}\`, which cannot be loaded from ${root}: ${error instanceof Error ? error.message : String(error)}`,
|
|
82
|
+
{ cause: error }
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
if (typeof loaded.before !== "function") {
|
|
86
|
+
throw new Error(
|
|
87
|
+
`sentinel build(nest): \`${name}\` exports no \`before\` factory, so it cannot run as a TypeScript transformer. Building without it would drop whatever it contributes.`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
const factory = loaded.before;
|
|
91
|
+
return factory(options, program);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
var decoratorMetadata = (options) => {
|
|
95
|
+
const typeAware = (options.transformers ?? []).length > 0;
|
|
96
|
+
let ts;
|
|
97
|
+
let program;
|
|
98
|
+
let compilerOptions;
|
|
99
|
+
let before = [];
|
|
100
|
+
const withoutTypes = [];
|
|
101
|
+
return {
|
|
102
|
+
name: "sentinel:decorator-metadata",
|
|
103
|
+
// Before Vite's own transform, so esbuild never sees the decorators it cannot handle, and so
|
|
104
|
+
// the source this plugin reads from the Program is still the source on disk.
|
|
105
|
+
enforce: "pre",
|
|
106
|
+
buildStart() {
|
|
107
|
+
ts = loadCompiler(options.root);
|
|
108
|
+
const config = readConfig(ts, options);
|
|
109
|
+
compilerOptions = config.compilerOptions;
|
|
110
|
+
if (!typeAware) return;
|
|
111
|
+
program = ts.createProgram(config.fileNames, config.compilerOptions);
|
|
112
|
+
before = loadTransformers(options.root, program, options.transformers ?? []);
|
|
113
|
+
},
|
|
114
|
+
transform(code, id) {
|
|
115
|
+
if (!TYPESCRIPT_SOURCE.test(id) || id.includes("node_modules")) return null;
|
|
116
|
+
if (ts === void 0 || compilerOptions === void 0) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`sentinel build(nest): the compiler was never prepared, so ${id} would be emitted with defaults rather than this module's tsconfig.`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
const sourceFile = program?.getSourceFile(id);
|
|
122
|
+
if (program === void 0 || sourceFile === void 0 || sourceFile.text !== code) {
|
|
123
|
+
if (typeAware) withoutTypes.push(id);
|
|
124
|
+
const output = ts.transpileModule(code, { fileName: id, compilerOptions });
|
|
125
|
+
return { code: output.outputText, map: output.sourceMapText ?? null };
|
|
126
|
+
}
|
|
127
|
+
let emitted;
|
|
128
|
+
let map;
|
|
129
|
+
program.emit(
|
|
130
|
+
sourceFile,
|
|
131
|
+
(fileName, text) => {
|
|
132
|
+
if (fileName.endsWith(".map")) map = text;
|
|
133
|
+
else emitted = text;
|
|
134
|
+
},
|
|
135
|
+
void 0,
|
|
136
|
+
false,
|
|
137
|
+
{ before }
|
|
138
|
+
);
|
|
139
|
+
if (emitted === void 0) {
|
|
140
|
+
throw new Error(`sentinel build(nest): TypeScript emitted nothing for ${id}.`);
|
|
141
|
+
}
|
|
142
|
+
return { code: emitted, map: map ?? null };
|
|
143
|
+
},
|
|
144
|
+
closeBundle() {
|
|
145
|
+
if (withoutTypes.length === 0) return;
|
|
146
|
+
const shown = withoutTypes.slice(0, 5).map((file) => path.relative(options.root, file));
|
|
147
|
+
this.warn(
|
|
148
|
+
`sentinel build(nest): ${withoutTypes.length} file(s) were emitted without type information, so their decorator metadata may differ from what ts-loader produced (${shown.join(", ")}${withoutTypes.length > shown.length ? ", \u2026" : ""}). They are outside this module's tsconfig, or another plugin rewrote them first.`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// src/roles/build/nest/tsconfig-aliases.ts
|
|
155
|
+
import { readFileSync } from "fs";
|
|
156
|
+
import { join } from "path";
|
|
157
|
+
var stripLineComments = (json) => json.replace(/^\s*\/\/.*$/gm, "");
|
|
158
|
+
var toAlias = (workspaceRoot, pattern, target) => {
|
|
159
|
+
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, "(.*)");
|
|
160
|
+
return {
|
|
161
|
+
find: new RegExp(`^${escaped}$`),
|
|
162
|
+
replacement: join(workspaceRoot, target.replace(/\*/g, "$1"))
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
var tsconfigAliases = (workspaceRoot, file = "tsconfig.base.json") => {
|
|
166
|
+
const raw = readFileSync(join(workspaceRoot, file), "utf8");
|
|
167
|
+
const { compilerOptions } = JSON.parse(stripLineComments(raw));
|
|
168
|
+
return Object.entries(compilerOptions?.paths ?? {}).flatMap(([pattern, targets]) => {
|
|
169
|
+
const [target] = targets;
|
|
170
|
+
return target === void 0 ? [] : [toAlias(workspaceRoot, pattern, target)];
|
|
171
|
+
}).sort((a, b) => b.find.source.length - a.find.source.length);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export {
|
|
175
|
+
decoratorMetadata,
|
|
176
|
+
tsconfigAliases
|
|
177
|
+
};
|
|
178
|
+
//# sourceMappingURL=chunk-3TDUIKVQ.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/roles/test/setup/msw.ts
|
|
2
|
+
import { setupServer } from "msw/node";
|
|
3
|
+
var server = setupServer();
|
|
4
|
+
function installMswLifecycle(hooks) {
|
|
5
|
+
hooks.beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
|
|
6
|
+
hooks.afterAll(() => server.close());
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
server,
|
|
11
|
+
installMswLifecycle
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=chunk-NKFGYPIQ.js.map
|