@hublo/sentinel 1.2.0-alpha.7 → 1.2.0-alpha.8
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.
|
@@ -50,6 +50,16 @@ interface NodeManifestOptions {
|
|
|
50
50
|
outDir: string;
|
|
51
51
|
/** Entry file name, written as `main` so `node .` resolves inside the image. */
|
|
52
52
|
entry?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Packages the IMAGE provides, which are therefore allowed to be required without being
|
|
55
|
+
* declared in the manifest.
|
|
56
|
+
*
|
|
57
|
+
* The generated Prisma clients are the case this exists for: the Dockerfile copies
|
|
58
|
+
* `node_modules/@prisma` from its own stage, so they resolve at runtime while no module
|
|
59
|
+
* declares them. Everything else that is required and undeclared is a bug, and the check
|
|
60
|
+
* below refuses the build.
|
|
61
|
+
*/
|
|
62
|
+
providedByImage?: readonly string[];
|
|
53
63
|
}
|
|
54
64
|
/**
|
|
55
65
|
* Writes the manifest and lockfile once the bundle is on disk.
|
|
@@ -80,28 +80,61 @@ var decoratorMetadata = (options) => {
|
|
|
80
80
|
|
|
81
81
|
// src/roles/build/nest/node-manifest.ts
|
|
82
82
|
import { existsSync as existsSync2, writeFileSync } from "fs";
|
|
83
|
+
import { isBuiltin } from "module";
|
|
83
84
|
import { join } from "path";
|
|
84
|
-
var
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
writeFileSync(join(options.outDir, "package.json"), `${JSON.stringify(manifest, null, 2)}
|
|
98
|
-
`);
|
|
99
|
-
writeFileSync(
|
|
100
|
-
join(options.outDir, getLockFileName("pnpm")),
|
|
101
|
-
createLockFile(manifest, graph, "pnpm")
|
|
102
|
-
);
|
|
85
|
+
var DEFAULT_PROVIDED_BY_IMAGE = ["@prisma"];
|
|
86
|
+
var externalPackages = (bundle) => {
|
|
87
|
+
const emitted = new Set(Object.keys(bundle));
|
|
88
|
+
const packages = /* @__PURE__ */ new Set();
|
|
89
|
+
for (const chunk of Object.values(bundle)) {
|
|
90
|
+
for (const imported of chunk.imports ?? []) {
|
|
91
|
+
if (emitted.has(imported)) continue;
|
|
92
|
+
if (imported.startsWith(".") || imported.startsWith("/")) continue;
|
|
93
|
+
if (isBuiltin(imported)) continue;
|
|
94
|
+
const parts = imported.split("/");
|
|
95
|
+
const name = imported.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
96
|
+
if (name) packages.add(name);
|
|
97
|
+
}
|
|
103
98
|
}
|
|
104
|
-
|
|
99
|
+
return packages;
|
|
100
|
+
};
|
|
101
|
+
var nodeManifest = (options) => {
|
|
102
|
+
let required = /* @__PURE__ */ new Set();
|
|
103
|
+
return {
|
|
104
|
+
name: "sentinel:node-manifest",
|
|
105
|
+
apply: "build",
|
|
106
|
+
generateBundle(_outputOptions, bundle) {
|
|
107
|
+
required = externalPackages(bundle);
|
|
108
|
+
},
|
|
109
|
+
async closeBundle() {
|
|
110
|
+
if (!existsSync2(options.outDir)) return;
|
|
111
|
+
const { createProjectGraphAsync } = await import("nx/src/devkit-exports.js");
|
|
112
|
+
const { createPackageJson, createLockFile, getLockFileName } = await import("@nx/js");
|
|
113
|
+
const graph = await createProjectGraphAsync({ exitOnError: false });
|
|
114
|
+
const manifest = createPackageJson(options.project, graph, {
|
|
115
|
+
root: options.workspaceRoot,
|
|
116
|
+
isProduction: true
|
|
117
|
+
});
|
|
118
|
+
manifest.main = options.entry ?? "main.js";
|
|
119
|
+
assertManifestCovers(required, manifest, options);
|
|
120
|
+
writeFileSync(join(options.outDir, "package.json"), `${JSON.stringify(manifest, null, 2)}
|
|
121
|
+
`);
|
|
122
|
+
writeFileSync(
|
|
123
|
+
join(options.outDir, getLockFileName("pnpm")),
|
|
124
|
+
createLockFile(manifest, graph, "pnpm")
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
var assertManifestCovers = (required, manifest, options) => {
|
|
130
|
+
const declared = new Set(Object.keys(manifest.dependencies ?? {}));
|
|
131
|
+
const provided = options.providedByImage ?? DEFAULT_PROVIDED_BY_IMAGE;
|
|
132
|
+
const missing = [...required].filter((name) => !declared.has(name)).filter((name) => !provided.some((prefix) => name === prefix || name.startsWith(`${prefix}/`))).sort();
|
|
133
|
+
if (missing.length === 0) return;
|
|
134
|
+
throw new Error(
|
|
135
|
+
`sentinel build(nest): the bundle requires ${missing.length} package(s) the generated manifest does not declare, so the image would not install them: ${missing.join(", ")}. Either declare them in the module's package.json, inline them with \`ssr.noExternal\`, or list them in \`providedByImage\` if the Dockerfile copies them in.`
|
|
136
|
+
);
|
|
137
|
+
};
|
|
105
138
|
|
|
106
139
|
// src/roles/build/nest/tsconfig-aliases.ts
|
|
107
140
|
import { readFileSync } from "fs";
|
|
@@ -137,6 +170,15 @@ var baseConfig = (options) => {
|
|
|
137
170
|
})
|
|
138
171
|
],
|
|
139
172
|
resolve: { alias: tsconfigAliases(options.workspaceRoot) },
|
|
173
|
+
ssr: {
|
|
174
|
+
// `importHelpers: true` in the workspace tsconfig makes TypeScript emit `require("tslib")`
|
|
175
|
+
// for `__decorate` and `__metadata`, so every decorated file depends on it. Hoisted into
|
|
176
|
+
// one scope Rollup inlined it and nobody noticed; preserving modules left it external, and
|
|
177
|
+
// the manifest nx generates from the project graph does not list it, because no module
|
|
178
|
+
// DECLARES tslib. The image therefore did not install it and the service died on its first
|
|
179
|
+
// require, only in the image: locally and in CI the workspace root has tslib.
|
|
180
|
+
noExternal: ["tslib"]
|
|
181
|
+
},
|
|
140
182
|
build: {
|
|
141
183
|
// An SSR build targets Node and leaves real packages external, which is what
|
|
142
184
|
// `webpack-node-externals` did: the image installs them from the generated manifest.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hublo/sentinel",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.8",
|
|
4
4
|
"description": "One CLI that guards code health across Hublo repos: shared lint/typescript/build/test presets, static & dynamic analysis, and architecture checks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|