@homepages/template-kit 0.9.0-dev-20260719224319 → 0.9.0
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 +29 -59
- package/README.md +14 -4
- package/dist/contracts/fill-treatments.d.ts +23 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/islands/discover.js +1 -1
- package/dist/islands/esbuild-plugin.js +1 -1
- package/dist/islands/wrap.d.ts +9 -1
- package/dist/media/resolve-fixture-media.d.ts +22 -0
- package/dist/package.js +1 -1
- package/dist/schema/resolve-section-ref.d.ts +4 -0
- package/dist/schema/rows.d.ts +9 -0
- package/dist/schema/section-nav.d.ts +5 -1
- package/dist/schema/slot-types.d.ts +1 -1
- package/dist/ssr.d.ts +7 -2
- package/dist/ssr.js +6 -2
- package/guide/INDEX.md +5 -0
- package/guide/check.md +1 -0
- package/guide/dev.md +1 -0
- package/guide/llms.txt +11 -3
- package/guide/upgrading.md +12 -8
- package/package.json +4 -16
- package/dist/canvas-core/chrome-css.d.ts +0 -24
- package/dist/canvas-core/chrome-css.js +0 -403
- package/dist/canvas-core/chrome.d.ts +0 -49
- package/dist/canvas-core/chrome.js +0 -147
- package/dist/canvas-core/frame.d.ts +0 -20
- package/dist/canvas-core/frame.js +0 -23
- package/dist/canvas-core/hidden-tabs.d.ts +0 -16
- package/dist/canvas-core/hidden-tabs.js +0 -84
- package/dist/canvas-core/resolve-target.js +0 -55
- package/dist/canvas-core/scroll.d.ts +0 -10
- package/dist/canvas-core/scroll.js +0 -15
- package/dist/canvas-core/section-dom.d.ts +0 -6
- package/dist/canvas-core/section-dom.js +0 -43
- package/dist/canvas-core/srcdoc.d.ts +0 -8
- package/dist/canvas-core/srcdoc.js +0 -77
- package/dist/canvas-core/types.d.ts +0 -76
- package/dist/canvas-core.d.ts +0 -9
- package/dist/canvas-core.js +0 -9
- package/dist/cli/check/config.js +0 -41
- package/dist/cli/check/css.js +0 -131
- package/dist/cli/check/diagnostics.js +0 -37
- package/dist/cli/check/index.js +0 -94
- package/dist/cli/check/loader.js +0 -149
- package/dist/cli/check/paths.js +0 -15
- package/dist/cli/check/relativize.js +0 -18
- package/dist/cli/check/render-invariants.js +0 -24
- package/dist/cli/check/resolve-tool.js +0 -38
- package/dist/cli/check/section-assets.js +0 -58
- package/dist/cli/check/stages/deps.js +0 -337
- package/dist/cli/check/stages/lint.js +0 -46
- package/dist/cli/check/stages/render.js +0 -98
- package/dist/cli/check/stages/size.js +0 -274
- package/dist/cli/check/stages/tree.js +0 -206
- package/dist/cli/check/stages/typecheck.js +0 -46
- package/dist/cli/check/stages/validate/catalog-exhaustiveness.js +0 -18
- package/dist/cli/check/stages/validate/facts.js +0 -19
- package/dist/cli/check/stages/validate/formatter-contract.js +0 -14
- package/dist/cli/check/stages/validate/group-contract.js +0 -21
- package/dist/cli/check/stages/validate/index.js +0 -66
- package/dist/cli/check/stages/validate/list-scalar-contract.js +0 -22
- package/dist/cli/check/stages/validate/nav-contract.js +0 -93
- package/dist/cli/check/stages/validate/nested-slot-contract.js +0 -12
- package/dist/cli/check/stages/validate/object-list-contract.js +0 -31
- package/dist/cli/check/stages/validate/orchestrator.js +0 -241
- package/dist/cli/check/stages/validate/produced-by-contract.js +0 -18
- package/dist/cli/check/stages/validate/row-contract.js +0 -31
- package/dist/cli/check/stages/validate/select-contract.js +0 -13
- package/dist/cli/check/stages/validate/source-contract.js +0 -21
- package/dist/cli/check/stages/validate/variant-contract.js +0 -15
- package/dist/cli/check/workspace.js +0 -99
- package/dist/cli/dev/build-css.js +0 -97
- package/dist/cli/dev/catalog.js +0 -35
- package/dist/cli/dev/compose-template.js +0 -39
- package/dist/cli/dev/content-override.js +0 -58
- package/dist/cli/dev/diagnostics.js +0 -68
- package/dist/cli/dev/discover.js +0 -35
- package/dist/cli/dev/fill-state.js +0 -77
- package/dist/cli/dev/index.js +0 -42
- package/dist/cli/dev/inspect.js +0 -38
- package/dist/cli/dev/island-bootstrap.js +0 -38
- package/dist/cli/dev/island-canvas-bootstrap.js +0 -204
- package/dist/cli/dev/island-map.js +0 -35
- package/dist/cli/dev/island-transform.js +0 -31
- package/dist/cli/dev/manifest-instances.js +0 -31
- package/dist/cli/dev/render-section.js +0 -24
- package/dist/cli/dev/screenshot-target.js +0 -24
- package/dist/cli/dev/section-page.js +0 -91
- package/dist/cli/dev/server.js +0 -511
- package/dist/cli/dev/slot-schema.js +0 -12
- package/dist/cli/dev/structure-summary.js +0 -119
- package/dist/cli/dev/tailwind.js +0 -32
- package/dist/cli/dev/tsconfig-paths-plugin.js +0 -90
- package/dist/cli/dev/vite-server.js +0 -39
- package/dist/cli/dev/workspace.js +0 -63
- package/dist/cli/link/index.js +0 -74
- package/dist/cli/link/overlay.js +0 -59
- package/dist/cli/link/watch.js +0 -25
- package/dist/cli/new/emit.js +0 -48
- package/dist/cli/new/index.js +0 -69
- package/dist/cli/new/scaffold/section/_Renderer.tsx +0 -20
- package/dist/cli/new/scaffold/section/_fill-spec.ts +0 -18
- package/dist/cli/new/scaffold/section/_fixtures.ts +0 -13
- package/dist/cli/new/scaffold/section/_schema.ts +0 -24
- package/dist/cli/new/scaffold/template/_manifest.json +0 -9
- package/dist/cli/new/scaffold/template/sections/hero/ExpandableText.tsx +0 -45
- package/dist/cli/new/scaffold/template/sections/hero/Renderer.tsx +0 -40
- package/dist/cli/new/scaffold/template/sections/hero/components/Feature.tsx +0 -14
- package/dist/cli/new/scaffold/template/sections/hero/fill-spec.ts +0 -29
- package/dist/cli/new/scaffold/template/sections/hero/fixtures.ts +0 -41
- package/dist/cli/new/scaffold/template/sections/hero/schema.ts +0 -60
- package/dist/cli/new/scaffold/template/theme.css +0 -24
- package/dist/cli/new/scaffold/template/theme.ts +0 -27
- package/dist/cli/new/scaffold-assets.js +0 -20
- package/dist/cli/pack/collect.js +0 -44
- package/dist/cli/pack/guards.js +0 -57
- package/dist/cli/pack/index.js +0 -66
- package/dist/cli/pack/manifest.js +0 -15
- package/dist/cli/pack/zip.js +0 -86
- package/dist/cli/theme/generate.js +0 -43
- package/dist/cli/theme/index.js +0 -33
- package/dist/cli/theme/load-theme.js +0 -69
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +0 -138
- package/dist/dev-client/assets/index-CJa9G1Na.css +0 -1
- package/dist/dev-client/assets/index-C_1atZs4.js +0 -450
- package/dist/dev-client/index.html +0 -13
- package/dist/tokens.css +0 -21
package/dist/cli/check/index.js
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { KIT_VERSION } from "../../index.js";
|
|
2
|
-
import { readLinkMarker } from "../link/overlay.js";
|
|
3
|
-
import { formatHuman, toJson } from "./diagnostics.js";
|
|
4
|
-
import { loadEsbuild } from "./resolve-tool.js";
|
|
5
|
-
import { resetLoadCache } from "./loader.js";
|
|
6
|
-
import { depsStage } from "./stages/deps.js";
|
|
7
|
-
import { lintStage } from "./stages/lint.js";
|
|
8
|
-
import { renderStage } from "./stages/render.js";
|
|
9
|
-
import { sizeStage } from "./stages/size.js";
|
|
10
|
-
import { treeStage } from "./stages/tree.js";
|
|
11
|
-
import { typecheckStage } from "./stages/typecheck.js";
|
|
12
|
-
import { validateStage } from "./stages/validate/index.js";
|
|
13
|
-
import { declaredKitPin, findWorkspaceRoot, selectTemplates, templateFromCwd } from "./workspace.js";
|
|
14
|
-
import { parseArgs } from "node:util";
|
|
15
|
-
|
|
16
|
-
//#region src/cli/check/index.ts
|
|
17
|
-
const STAGES = [
|
|
18
|
-
typecheckStage,
|
|
19
|
-
lintStage,
|
|
20
|
-
validateStage,
|
|
21
|
-
treeStage,
|
|
22
|
-
renderStage,
|
|
23
|
-
sizeStage
|
|
24
|
-
];
|
|
25
|
-
/**
|
|
26
|
-
* A run that gated nothing must never report success. `every()` over an empty set
|
|
27
|
-
* is vacuously true, so a selection of zero templates printed "check passed" and
|
|
28
|
-
* exited 0 having run not one gate — the worst failure this command has, since
|
|
29
|
-
* ingest re-runs it and would accept the submission on that same silence.
|
|
30
|
-
*/
|
|
31
|
-
function emptySelectionDiagnostics(selected, all) {
|
|
32
|
-
if (selected.length > 0) return [];
|
|
33
|
-
return [{
|
|
34
|
-
ruleId: "template-kit/no-templates",
|
|
35
|
-
template: "",
|
|
36
|
-
file: "templates",
|
|
37
|
-
message: all ? "check --all found no template to check: templates/ holds no template folder." : "This workspace has no template to check: templates/ holds no template folder.",
|
|
38
|
-
fix: "Add a template: templates/<key>/ with a manifest.json declaring its section composition and a sections/ folder holding the sections it composes."
|
|
39
|
-
}];
|
|
40
|
-
}
|
|
41
|
-
async function checkTemplate(root, template) {
|
|
42
|
-
const diagnostics = [];
|
|
43
|
-
for (const stage of STAGES) diagnostics.push(...await stage(root, template));
|
|
44
|
-
return {
|
|
45
|
-
ok: diagnostics.length === 0,
|
|
46
|
-
diagnostics
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
async function runCheck(argv, cwd) {
|
|
50
|
-
resetLoadCache();
|
|
51
|
-
const { values, positionals } = parseArgs({
|
|
52
|
-
args: argv,
|
|
53
|
-
options: {
|
|
54
|
-
all: {
|
|
55
|
-
type: "boolean",
|
|
56
|
-
default: false
|
|
57
|
-
},
|
|
58
|
-
json: {
|
|
59
|
-
type: "boolean",
|
|
60
|
-
default: false
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
allowPositionals: true
|
|
64
|
-
});
|
|
65
|
-
const root = await findWorkspaceRoot(cwd);
|
|
66
|
-
const selected = await selectTemplates(root, positionals[0] ?? (values.all ? void 0 : templateFromCwd(root, cwd)), values.all, "check");
|
|
67
|
-
if (selected.length > 0) await loadEsbuild(root);
|
|
68
|
-
const templates = {};
|
|
69
|
-
for (const template of selected) templates[template.key] = await checkTemplate(root, template);
|
|
70
|
-
const workspaceDiagnostics = [...await depsStage(root), ...emptySelectionDiagnostics(selected, values.all)];
|
|
71
|
-
const workspace = {
|
|
72
|
-
ok: workspaceDiagnostics.length === 0,
|
|
73
|
-
diagnostics: workspaceDiagnostics
|
|
74
|
-
};
|
|
75
|
-
const marker = await readLinkMarker(root);
|
|
76
|
-
const report = {
|
|
77
|
-
ok: workspace.ok && Object.values(templates).every((t) => t.ok),
|
|
78
|
-
kitVersion: KIT_VERSION,
|
|
79
|
-
...marker === void 0 ? {} : { linkedKit: {
|
|
80
|
-
linkedFrom: marker.linkedFrom,
|
|
81
|
-
kitVersion: marker.kitVersion,
|
|
82
|
-
pin: await declaredKitPin(root)
|
|
83
|
-
} },
|
|
84
|
-
templates,
|
|
85
|
-
workspace
|
|
86
|
-
};
|
|
87
|
-
return {
|
|
88
|
-
code: report.ok ? 0 : 1,
|
|
89
|
-
output: values.json ? toJson(report) : formatHuman(report)
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
//#endregion
|
|
94
|
-
export { runCheck };
|
package/dist/cli/check/loader.js
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import { loadEsbuild } from "./resolve-tool.js";
|
|
2
|
-
import { ASSET_LOADERS, ASSET_NAMES } from "./css.js";
|
|
3
|
-
import { mkdir, rm, stat } from "node:fs/promises";
|
|
4
|
-
import { join, relative } from "node:path";
|
|
5
|
-
import { createHash } from "node:crypto";
|
|
6
|
-
import { pathToFileURL } from "node:url";
|
|
7
|
-
|
|
8
|
-
//#region src/cli/check/loader.ts
|
|
9
|
-
const CONTRACT_FILES = [
|
|
10
|
-
"Renderer.tsx",
|
|
11
|
-
"schema.ts",
|
|
12
|
-
"fill-spec.ts",
|
|
13
|
-
"fixtures.ts"
|
|
14
|
-
];
|
|
15
|
-
const EXPECTED_EXPORT = {
|
|
16
|
-
"Renderer.tsx": "default",
|
|
17
|
-
"schema.ts": "schema",
|
|
18
|
-
"fill-spec.ts": "fillSpec",
|
|
19
|
-
"fixtures.ts": "default"
|
|
20
|
-
};
|
|
21
|
-
/**
|
|
22
|
-
* A section is missing one or more of the four contract files. Typed (not a bare
|
|
23
|
-
* Error) because exactly one stage owns this failure: the tree stage's
|
|
24
|
-
* `bundle-incomplete`, which names the missing file and where it belongs. Every
|
|
25
|
-
* other stage swallows this cause rather than re-reporting the same root problem
|
|
26
|
-
* under a second rule id.
|
|
27
|
-
*/
|
|
28
|
-
var MissingContractFile = class extends Error {
|
|
29
|
-
files;
|
|
30
|
-
constructor(files) {
|
|
31
|
-
super(`Section is missing its contract file(s): ${files.join(", ")}.`);
|
|
32
|
-
this.files = files;
|
|
33
|
-
this.name = "MissingContractFile";
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
/** A contract file built, but does not export the binding the contract requires. `file` is workspace-relative. */
|
|
37
|
-
var MissingSectionExport = class extends Error {
|
|
38
|
-
file;
|
|
39
|
-
binding;
|
|
40
|
-
constructor(file, binding) {
|
|
41
|
-
super(binding === "default" ? `${file} has no default export. It must export the section's ${file.endsWith("Renderer.tsx") ? "renderer component" : "fixture module"} as its default export.` : `${file} does not export "${binding}". It must export a "${binding}" binding.`);
|
|
42
|
-
this.file = file;
|
|
43
|
-
this.binding = binding;
|
|
44
|
-
this.name = "MissingSectionExport";
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
const RESOLVING = Symbol("css-stub:resolving");
|
|
48
|
-
const isUnderNodeModules = (path) => path.split(/[/\\]/).includes("node_modules");
|
|
49
|
-
const cssStub = {
|
|
50
|
-
name: "css-stub",
|
|
51
|
-
setup(b) {
|
|
52
|
-
b.onResolve({ filter: /.*/ }, async (args) => {
|
|
53
|
-
if (args.pluginData === RESOLVING) return null;
|
|
54
|
-
const resolved = await b.resolve(args.path, {
|
|
55
|
-
importer: args.importer,
|
|
56
|
-
namespace: args.namespace,
|
|
57
|
-
resolveDir: args.resolveDir,
|
|
58
|
-
kind: args.kind,
|
|
59
|
-
pluginData: RESOLVING
|
|
60
|
-
});
|
|
61
|
-
if (resolved.errors.length === 0 && resolved.path !== "" && resolved.path.endsWith(".css")) return {
|
|
62
|
-
path: resolved.path,
|
|
63
|
-
namespace: "css-stub"
|
|
64
|
-
};
|
|
65
|
-
if (!args.path.startsWith(".") && !args.path.startsWith("/")) {
|
|
66
|
-
if (resolved.errors.length > 0 || isUnderNodeModules(resolved.path)) return {
|
|
67
|
-
path: args.path,
|
|
68
|
-
external: true
|
|
69
|
-
};
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
return null;
|
|
73
|
-
});
|
|
74
|
-
b.onLoad({
|
|
75
|
-
filter: /.*/,
|
|
76
|
-
namespace: "css-stub"
|
|
77
|
-
}, () => ({
|
|
78
|
-
contents: "",
|
|
79
|
-
loader: "js"
|
|
80
|
-
}));
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
const loadCache = /* @__PURE__ */ new Map();
|
|
84
|
-
/** Drops every memoized section load. Called once per `runCheck`; see the note above. */
|
|
85
|
-
function resetLoadCache() {
|
|
86
|
-
loadCache.clear();
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Transpile + evaluate a section's four contract modules. Throws `MissingContractFile`
|
|
90
|
-
* when one of the four is absent, `MissingSectionExport` when one built but does not
|
|
91
|
-
* export its required binding, and a plain `Error` (esbuild's, path-relativized by the
|
|
92
|
-
* caller) when one fails to build.
|
|
93
|
-
*/
|
|
94
|
-
function loadSection(workspaceRoot, section) {
|
|
95
|
-
const outdir = join(workspaceRoot, "node_modules", ".template-kit", "check", createHash("sha256").update(section.dir).digest("hex").slice(0, 12));
|
|
96
|
-
const cached = loadCache.get(outdir);
|
|
97
|
-
if (cached) return cached;
|
|
98
|
-
const promise = buildAndLoad(section, outdir, workspaceRoot);
|
|
99
|
-
loadCache.set(outdir, promise);
|
|
100
|
-
return promise;
|
|
101
|
-
}
|
|
102
|
-
const isFile = async (p) => {
|
|
103
|
-
try {
|
|
104
|
-
return (await stat(p)).isFile();
|
|
105
|
-
} catch {
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
async function buildAndLoad(section, outdir, workspaceRoot) {
|
|
110
|
-
const missing = [];
|
|
111
|
-
for (const file of CONTRACT_FILES) if (!await isFile(join(section.dir, file))) missing.push(file);
|
|
112
|
-
if (missing.length > 0) throw new MissingContractFile(missing);
|
|
113
|
-
await rm(outdir, {
|
|
114
|
-
recursive: true,
|
|
115
|
-
force: true
|
|
116
|
-
});
|
|
117
|
-
await mkdir(outdir, { recursive: true });
|
|
118
|
-
const { build } = await loadEsbuild(workspaceRoot);
|
|
119
|
-
await build({
|
|
120
|
-
entryPoints: CONTRACT_FILES.map((f) => join(section.dir, f)),
|
|
121
|
-
outdir,
|
|
122
|
-
bundle: true,
|
|
123
|
-
splitting: true,
|
|
124
|
-
format: "esm",
|
|
125
|
-
platform: "neutral",
|
|
126
|
-
jsx: "automatic",
|
|
127
|
-
logLevel: "silent",
|
|
128
|
-
plugins: [cssStub],
|
|
129
|
-
loader: { ...ASSET_LOADERS },
|
|
130
|
-
assetNames: ASSET_NAMES,
|
|
131
|
-
absWorkingDir: workspaceRoot
|
|
132
|
-
});
|
|
133
|
-
const load = async (source, emitted) => {
|
|
134
|
-
const mod = await import(pathToFileURL(join(outdir, emitted)).href);
|
|
135
|
-
const binding = EXPECTED_EXPORT[source];
|
|
136
|
-
const value = mod[binding];
|
|
137
|
-
if (value === void 0) throw new MissingSectionExport(relative(workspaceRoot, join(section.dir, source)), binding);
|
|
138
|
-
return value;
|
|
139
|
-
};
|
|
140
|
-
return {
|
|
141
|
-
Renderer: await load("Renderer.tsx", "Renderer.js"),
|
|
142
|
-
schema: await load("schema.ts", "schema.js"),
|
|
143
|
-
fillSpec: await load("fill-spec.ts", "fill-spec.js"),
|
|
144
|
-
fixtures: await load("fixtures.ts", "fixtures.js")
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
//#endregion
|
|
149
|
-
export { CONTRACT_FILES, MissingContractFile, MissingSectionExport, cssStub, loadSection, resetLoadCache };
|
package/dist/cli/check/paths.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { relative, sep } from "node:path";
|
|
2
|
-
|
|
3
|
-
//#region src/cli/check/paths.ts
|
|
4
|
-
/** `file` is `dir` itself or nested under it. */
|
|
5
|
-
function isUnder(dir, file) {
|
|
6
|
-
const rel = relative(dir, file);
|
|
7
|
-
return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep}`);
|
|
8
|
-
}
|
|
9
|
-
/** The section `file` sits inside, or `undefined` when it isn't under any of the template's sections. */
|
|
10
|
-
function sectionOf(template, file) {
|
|
11
|
-
return template.sections.find((section) => isUnder(section.dir, file))?.name;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
//#endregion
|
|
15
|
-
export { isUnder, sectionOf };
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { resolve, sep } from "node:path";
|
|
2
|
-
|
|
3
|
-
//#region src/cli/check/relativize.ts
|
|
4
|
-
function escapeRegExp(s) {
|
|
5
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Rewrites every occurrence of `workspaceRoot` in a tool-sourced message to the
|
|
9
|
-
* path an author would type: workspace-relative, or "." for the root itself.
|
|
10
|
-
* Idempotent, and a no-op on a message that names no path.
|
|
11
|
-
*/
|
|
12
|
-
function relativizePaths(message, workspaceRoot) {
|
|
13
|
-
const root = resolve(workspaceRoot);
|
|
14
|
-
return message.replace(new RegExp(`${escapeRegExp(root)}${escapeRegExp(sep)}`, "g"), "").replace(new RegExp(escapeRegExp(root), "g"), ".");
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
//#endregion
|
|
18
|
-
export { relativizePaths };
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
//#region src/cli/check/render-invariants.ts
|
|
2
|
-
function checkRenderInvariants(html) {
|
|
3
|
-
const violations = [];
|
|
4
|
-
if (html.includes("[object Object]")) violations.push({
|
|
5
|
-
rule: "sentinel-text",
|
|
6
|
-
detail: "rendered text contains \"[object Object]\""
|
|
7
|
-
});
|
|
8
|
-
for (const m of html.matchAll(/>\s*(null|undefined|NaN)\s*</g)) violations.push({
|
|
9
|
-
rule: "sentinel-text",
|
|
10
|
-
detail: `rendered leaf is the bare sentinel "${m[1]}"`
|
|
11
|
-
});
|
|
12
|
-
for (const m of html.matchAll(/<img\b[^>]*>/g)) {
|
|
13
|
-
const attr = /\ssrc\s*=\s*(?:"([^"]*)"|'([^']*)')/.exec(m[0]);
|
|
14
|
-
const src = attr ? attr[1] ?? attr[2] : void 0;
|
|
15
|
-
if (src === void 0 || src.trim() === "" || src === "undefined" || src === "null") violations.push({
|
|
16
|
-
rule: "broken-image",
|
|
17
|
-
detail: `<img> with ${src === void 0 ? "no src" : `src="${src}"`}`
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
return violations;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
//#endregion
|
|
24
|
-
export { checkRenderInvariants };
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
|
-
import { pathToFileURL } from "node:url";
|
|
3
|
-
import { createRequire } from "node:module";
|
|
4
|
-
|
|
5
|
-
//#region src/cli/check/resolve-tool.ts
|
|
6
|
-
/**
|
|
7
|
-
* Loads `specifier` exactly as code running in the workspace at `root` would:
|
|
8
|
-
* resolved from that workspace's own `package.json`, never from this
|
|
9
|
-
* package's dependency tree. A missing install is a clean, actionable
|
|
10
|
-
* `Error` — never a bare `MODULE_NOT_FOUND` stack trace.
|
|
11
|
-
*/
|
|
12
|
-
async function resolveFromWorkspace(root, specifier) {
|
|
13
|
-
const require = createRequire(pathToFileURL(join(root, "package.json")));
|
|
14
|
-
let resolved;
|
|
15
|
-
try {
|
|
16
|
-
resolved = require.resolve(specifier);
|
|
17
|
-
} catch {
|
|
18
|
-
throw new Error(`${specifier} is not installed in this workspace. Install it: npm i -D ${specifier}`);
|
|
19
|
-
}
|
|
20
|
-
return await import(pathToFileURL(resolved).href);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* The workspace's esbuild — `check` transpiles a section's contract modules
|
|
24
|
-
* (loader.ts), bundles its package CSS (css.ts) and measures its published bytes
|
|
25
|
-
* (stages/size.ts) with it.
|
|
26
|
-
*
|
|
27
|
-
* `typeof import("esbuild")` is a TYPE-only import: it is erased at build time, so
|
|
28
|
-
* this module never carries a static `esbuild` import into the shipped `dist/`, and
|
|
29
|
-
* a consumer that only renders sections never installs it. Every call site must
|
|
30
|
-
* `await` this rather than importing `build` at module scope — a static import would
|
|
31
|
-
* put esbuild back on the critical path of `import "@homepages/template-kit"`.
|
|
32
|
-
*/
|
|
33
|
-
function loadEsbuild(root) {
|
|
34
|
-
return resolveFromWorkspace(root, "esbuild");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
//#endregion
|
|
38
|
-
export { loadEsbuild, resolveFromWorkspace };
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { loadEsbuild } from "./resolve-tool.js";
|
|
2
|
-
import { ASSET_LOADERS, ASSET_NAMES, ASSET_PUBLIC_PATH, sectionCssFiles } from "./css.js";
|
|
3
|
-
import { isUnder } from "./paths.js";
|
|
4
|
-
import { extname, join, resolve } from "node:path";
|
|
5
|
-
|
|
6
|
-
//#region src/cli/check/section-assets.ts
|
|
7
|
-
const ASSET_EXTS = new Set(Object.keys(ASSET_LOADERS));
|
|
8
|
-
async function referencedSectionAssets(workspaceRoot, section) {
|
|
9
|
-
const { build } = await loadEsbuild(workspaceRoot);
|
|
10
|
-
const referenced = /* @__PURE__ */ new Set();
|
|
11
|
-
const collect = (inputs) => {
|
|
12
|
-
for (const key of Object.keys(inputs ?? {})) {
|
|
13
|
-
const abs = resolve(workspaceRoot, key);
|
|
14
|
-
if (ASSET_EXTS.has(extname(abs).toLowerCase()) && isUnder(section.dir, abs)) referenced.add(abs);
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
collect((await build({
|
|
18
|
-
entryPoints: [join(section.dir, "Renderer.tsx")],
|
|
19
|
-
bundle: true,
|
|
20
|
-
write: false,
|
|
21
|
-
metafile: true,
|
|
22
|
-
format: "esm",
|
|
23
|
-
platform: "browser",
|
|
24
|
-
jsx: "automatic",
|
|
25
|
-
outdir: "dist",
|
|
26
|
-
external: [
|
|
27
|
-
"react",
|
|
28
|
-
"react-dom",
|
|
29
|
-
"react-dom/server",
|
|
30
|
-
"react/jsx-runtime",
|
|
31
|
-
"react/jsx-dev-runtime"
|
|
32
|
-
],
|
|
33
|
-
loader: { ...ASSET_LOADERS },
|
|
34
|
-
publicPath: ASSET_PUBLIC_PATH,
|
|
35
|
-
assetNames: ASSET_NAMES,
|
|
36
|
-
absWorkingDir: workspaceRoot,
|
|
37
|
-
logLevel: "silent"
|
|
38
|
-
})).metafile?.inputs);
|
|
39
|
-
for (const cssFile of await sectionCssFiles(section.dir)) collect((await build({
|
|
40
|
-
entryPoints: [cssFile],
|
|
41
|
-
bundle: true,
|
|
42
|
-
write: false,
|
|
43
|
-
metafile: true,
|
|
44
|
-
outdir: "dist",
|
|
45
|
-
loader: {
|
|
46
|
-
".css": "css",
|
|
47
|
-
...ASSET_LOADERS
|
|
48
|
-
},
|
|
49
|
-
publicPath: ASSET_PUBLIC_PATH,
|
|
50
|
-
assetNames: ASSET_NAMES,
|
|
51
|
-
absWorkingDir: workspaceRoot,
|
|
52
|
-
logLevel: "silent"
|
|
53
|
-
})).metafile?.inputs);
|
|
54
|
-
return referenced;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
//#endregion
|
|
58
|
-
export { referencedSectionAssets };
|