lineadraw 0.4.0 → 0.6.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/README.md +218 -13
- package/bin/lineadraw.mjs +277 -97
- package/dist/chunk-L4FNH35X.js +80 -0
- package/dist/chunk-L4FNH35X.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/embedded-F5ZJYFHC.js +10 -0
- package/dist/embedded-F5ZJYFHC.js.map +1 -0
- package/dist/node-OWPSMNLK.js +255 -0
- package/dist/node-OWPSMNLK.js.map +1 -0
- package/dist/pkg/linea_dwg_bg.wasm +0 -0
- package/dist/sdk.d.ts +5816 -0
- package/dist/sdk.js +15756 -0
- package/dist/sdk.js.map +1 -0
- package/lib/init.mjs +42 -5
- package/lib/manifest.mjs +65 -27
- package/lineadraw.d.ts +1386 -167
- package/package.json +34 -6
- package/templates/README.md +18 -18
- package/templates/tsconfig.json +2 -0
package/lib/init.mjs
CHANGED
|
@@ -12,7 +12,13 @@ import {
|
|
|
12
12
|
} from "node:fs";
|
|
13
13
|
import { dirname, join } from "node:path";
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
buildManifest,
|
|
17
|
+
manifestJson,
|
|
18
|
+
MANIFEST_PATH,
|
|
19
|
+
verifyWithSdk,
|
|
20
|
+
withThumbnails,
|
|
21
|
+
} from "./manifest.mjs";
|
|
16
22
|
|
|
17
23
|
const templatesDir = join(
|
|
18
24
|
dirname(fileURLToPath(import.meta.url)),
|
|
@@ -33,8 +39,29 @@ export const collectionSlug = (pkgName) => {
|
|
|
33
39
|
return slug === "" ? "my-collection" : slug;
|
|
34
40
|
};
|
|
35
41
|
|
|
36
|
-
/**
|
|
37
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Warn when an existing tsconfig lets TypeScript default `lib` to include
|
|
44
|
+
* DOM: lib.dom's globals collide with lineadraw's global DTO aliases —
|
|
45
|
+
* DOM's `interface Text` (a Node) silently wins over lineadraw's `Text`
|
|
46
|
+
* (skipLibCheck hides the duplicate-identifier error). Repos scaffolded
|
|
47
|
+
* before lineadraw 0.3.1 carry such a tsconfig. Cheap string test on
|
|
48
|
+
* purpose: tsconfig is JSONC, so JSON.parse can't be trusted here.
|
|
49
|
+
* Returns the warning text, or null when the config is fine or absent.
|
|
50
|
+
*/
|
|
51
|
+
export const tsconfigLibWarning = (dir) => {
|
|
52
|
+
const p = join(dir, "tsconfig.json");
|
|
53
|
+
if (!existsSync(p)) return null;
|
|
54
|
+
const text = readFileSync(p, "utf8");
|
|
55
|
+
if (/"lib"\s*:/.test(text)) return null;
|
|
56
|
+
return (
|
|
57
|
+
'tsconfig.json has no "lib" — TypeScript then includes DOM types, and ' +
|
|
58
|
+
"DOM's global Text/Image/... shadow the lineadraw script types " +
|
|
59
|
+
'(Text becomes a DOM Node). Add to compilerOptions: "lib": ["ES2022"], "types": []'
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** Run init in `dir`. Returns `{ created, skipped, warnings }` lists. */
|
|
64
|
+
export const init = async (dir) => {
|
|
38
65
|
const pkgPath = join(dir, "package.json");
|
|
39
66
|
if (!existsSync(pkgPath))
|
|
40
67
|
throw new Error(
|
|
@@ -112,16 +139,26 @@ export const init = (dir) => {
|
|
|
112
139
|
write(".gitignore", "node_modules/\n");
|
|
113
140
|
|
|
114
141
|
// Generate the catalog last, over whatever modules now exist.
|
|
115
|
-
const { manifest, problems } = buildManifest(dir);
|
|
142
|
+
const { manifest: base, sources, problems } = buildManifest(dir);
|
|
116
143
|
if (problems.length > 0) {
|
|
117
144
|
const detail = problems
|
|
118
145
|
.map((p) => `${p.file}: ${p.problems.join("; ")}`)
|
|
119
146
|
.join("\n");
|
|
120
147
|
throw new Error(`existing modules failed validation:\n${detail}`);
|
|
121
148
|
}
|
|
149
|
+
// Bake thumbnails now, so the very first commit's manifest matches what
|
|
150
|
+
// CI's `check` regenerates.
|
|
151
|
+
const sdkResult = await verifyWithSdk(base, sources);
|
|
152
|
+
const manifest = withThumbnails(base, sdkResult.thumbnails);
|
|
122
153
|
mkdirSync(join(dir, dirname(MANIFEST_PATH)), { recursive: true });
|
|
123
154
|
writeFileSync(join(dir, MANIFEST_PATH), manifestJson(manifest));
|
|
124
155
|
created.push(`${MANIFEST_PATH} (${manifest.entries.length} entries)`);
|
|
125
156
|
|
|
126
|
-
|
|
157
|
+
// A kept (pre-existing) tsconfig may carry the DOM-collision footgun the
|
|
158
|
+
// template avoids — surface it instead of silently leaving it broken.
|
|
159
|
+
const warnings = [];
|
|
160
|
+
const libWarning = tsconfigLibWarning(dir);
|
|
161
|
+
if (libWarning !== null) warnings.push(libWarning);
|
|
162
|
+
|
|
163
|
+
return { created, skipped, warnings };
|
|
127
164
|
};
|
package/lib/manifest.mjs
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
8
8
|
import { join } from "node:path";
|
|
9
|
-
import { pathToFileURL } from "node:url";
|
|
10
9
|
|
|
11
10
|
// sucrase is this package's own dependency — the light path needs nothing
|
|
12
11
|
// else installed.
|
|
@@ -19,7 +18,9 @@ const SEMVER_RE = /^\d+\.\d+\.\d+$/;
|
|
|
19
18
|
export const readConfig = (repoDir) => {
|
|
20
19
|
const pkgPath = join(repoDir, "package.json");
|
|
21
20
|
if (!existsSync(pkgPath))
|
|
22
|
-
throw new Error(
|
|
21
|
+
throw new Error(
|
|
22
|
+
`no package.json in ${repoDir} — run \`lineadraw init\` in an npm project`,
|
|
23
|
+
);
|
|
23
24
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
24
25
|
const cfg = pkg.lineadraw ?? {};
|
|
25
26
|
return {
|
|
@@ -142,17 +143,17 @@ export const buildManifest = (repoDir) => {
|
|
|
142
143
|
entryOf("command", repoDir, cfg.commandsDir, f),
|
|
143
144
|
),
|
|
144
145
|
];
|
|
145
|
-
const problems = results
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
const problems = results
|
|
147
|
+
.filter((r) => r.problems)
|
|
148
|
+
.map((r) => ({
|
|
149
|
+
file: r.file,
|
|
150
|
+
problems: r.problems,
|
|
151
|
+
}));
|
|
149
152
|
const entries = results.filter((r) => r.entry).map((r) => r.entry);
|
|
150
153
|
|
|
151
154
|
const dupes = [
|
|
152
155
|
...new Set(
|
|
153
|
-
entries
|
|
154
|
-
.map((e) => e.id)
|
|
155
|
-
.filter((id, i, all) => all.indexOf(id) !== i),
|
|
156
|
+
entries.map((e) => e.id).filter((id, i, all) => all.indexOf(id) !== i),
|
|
156
157
|
),
|
|
157
158
|
];
|
|
158
159
|
if (dupes.length > 0)
|
|
@@ -167,9 +168,7 @@ export const buildManifest = (repoDir) => {
|
|
|
167
168
|
entries.filter((e) => e.kind === "block").map((e) => e.id),
|
|
168
169
|
);
|
|
169
170
|
const dangling = entries.flatMap((e) =>
|
|
170
|
-
e.dependencies
|
|
171
|
-
.filter((d) => !blockIds.has(d))
|
|
172
|
-
.map((d) => `${e.id} → ${d}`),
|
|
171
|
+
e.dependencies.filter((d) => !blockIds.has(d)).map((d) => `${e.id} → ${d}`),
|
|
173
172
|
);
|
|
174
173
|
if (dangling.length > 0)
|
|
175
174
|
problems.push({
|
|
@@ -197,29 +196,68 @@ export const manifestJson = (manifest) =>
|
|
|
197
196
|
export const MANIFEST_PATH = ".lineadraw/marketplace.json";
|
|
198
197
|
|
|
199
198
|
/**
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
* `{ ran, problems }` — `ran: false`
|
|
203
|
-
*
|
|
199
|
+
* Execution-level verification: dry-run every block through this package's
|
|
200
|
+
* own SDK (dist/sdk.js) and bake preview thumbnails. Returns
|
|
201
|
+
* `{ ran, problems, thumbnails }` — `ran: false` (reason "load-failed") when
|
|
202
|
+
* the SDK can't load (an unbuilt checkout, a broken native install);
|
|
203
|
+
* validation-only, so the manifest's base CONTENT never depends on it.
|
|
204
|
+
* `loadSdk` is injectable for tests.
|
|
204
205
|
*/
|
|
205
|
-
export const verifyWithSdk = async (
|
|
206
|
+
export const verifyWithSdk = async (
|
|
207
|
+
manifest,
|
|
208
|
+
sources,
|
|
209
|
+
loadSdk = () => import("../dist/sdk.js"),
|
|
210
|
+
) => {
|
|
206
211
|
let sdk;
|
|
207
212
|
try {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
213
|
+
sdk = await loadSdk();
|
|
214
|
+
} catch (e) {
|
|
215
|
+
return {
|
|
216
|
+
ran: false,
|
|
217
|
+
problems: [],
|
|
218
|
+
reason: "load-failed",
|
|
219
|
+
detail: e instanceof Error ? e.message : String(e),
|
|
220
|
+
};
|
|
214
221
|
}
|
|
215
|
-
if (typeof sdk.testBlockDefinition !== "function")
|
|
216
|
-
return { ran: false, problems: [] }; // sdk predates module validation
|
|
217
222
|
const problems = [];
|
|
223
|
+
// All block sources double as the resolution set for thumbnails, so a
|
|
224
|
+
// block emitting instances of its catalog siblings previews complete.
|
|
225
|
+
const blockSources = manifest.entries
|
|
226
|
+
.filter((e) => e.kind === "block")
|
|
227
|
+
.map((e) => sources.get(e.id));
|
|
228
|
+
const thumbnails = new Map();
|
|
218
229
|
for (const entry of manifest.entries) {
|
|
219
230
|
if (entry.kind !== "block") continue; // commands are interactive — no dry run
|
|
220
231
|
const result = await sdk.testBlockDefinition(sources.get(entry.id));
|
|
221
|
-
if (!result.ok)
|
|
232
|
+
if (!result.ok) {
|
|
222
233
|
problems.push({ file: entry.source, problems: [result.error] });
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
// Bake the preview thumbnail. The render is deterministic, so the
|
|
237
|
+
// manifest stays byte-stable.
|
|
238
|
+
try {
|
|
239
|
+
const svg = await sdk.renderBlockThumbnailSvg(sources.get(entry.id), {
|
|
240
|
+
definitions: blockSources,
|
|
241
|
+
});
|
|
242
|
+
if (typeof svg === "string") thumbnails.set(entry.id, svg);
|
|
243
|
+
} catch {
|
|
244
|
+
// A block that validates but can't render just ships no thumbnail.
|
|
245
|
+
}
|
|
223
246
|
}
|
|
224
|
-
return { ran: true, problems };
|
|
247
|
+
return { ran: true, problems, thumbnails };
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Manifest with `thumbnailSvg` baked onto the block entries that have one —
|
|
252
|
+
* appended as the LAST key of each entry so serialization stays stable.
|
|
253
|
+
*/
|
|
254
|
+
export const withThumbnails = (manifest, thumbnails) => {
|
|
255
|
+
if (!thumbnails || thumbnails.size === 0) return manifest;
|
|
256
|
+
return {
|
|
257
|
+
...manifest,
|
|
258
|
+
entries: manifest.entries.map((entry) => {
|
|
259
|
+
const svg = thumbnails.get(entry.id);
|
|
260
|
+
return svg === undefined ? entry : { ...entry, thumbnailSvg: svg };
|
|
261
|
+
}),
|
|
262
|
+
};
|
|
225
263
|
};
|