lineadraw 0.5.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/lib/init.mjs CHANGED
@@ -146,9 +146,9 @@ export const init = async (dir) => {
146
146
  .join("\n");
147
147
  throw new Error(`existing modules failed validation:\n${detail}`);
148
148
  }
149
- // Bake thumbnails when the sdk is installed, so the very first commit's
150
- // manifest matches what CI's `check` (sdk installed) regenerates.
151
- const sdkResult = await verifyWithSdk(dir, base, sources);
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
152
  const manifest = withThumbnails(base, sdkResult.thumbnails);
153
153
  mkdirSync(join(dir, dirname(MANIFEST_PATH)), { recursive: true });
154
154
  writeFileSync(join(dir, MANIFEST_PATH), manifestJson(manifest));
package/lib/manifest.mjs CHANGED
@@ -5,8 +5,7 @@
5
5
  // bytes, so `check` is a plain diff.
6
6
  import { createRequire } from "node:module";
7
7
  import { existsSync, readFileSync, readdirSync } from "node:fs";
8
- import { dirname, join, resolve } from "node:path";
9
- import { pathToFileURL } from "node:url";
8
+ import { join } from "node:path";
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(`no package.json in ${repoDir} — run \`lineadraw init\` in an npm project`);
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.filter((r) => r.problems).map((r) => ({
146
- file: r.file,
147
- problems: r.problems,
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,51 +196,22 @@ export const manifestJson = (manifest) =>
197
196
  export const MANIFEST_PATH = ".lineadraw/marketplace.json";
198
197
 
199
198
  /**
200
- * Optional execution-level verification: dry-run every block through
201
- * @lineadraw/sdk when the repo has it installed. Returns
202
- * `{ ran, problems }` — `ran: false` when the sdk (or a new enough one) is
203
- * absent; validation-only, so manifest CONTENT never depends on it.
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
- /**
206
- * Locate the repo's installed @lineadraw/sdk entry module. require.resolve
207
- * alone is NOT enough: sdk ≤ 0.11 exports only the `import` condition,
208
- * which a CJS resolver refuses (ERR_PACKAGE_PATH_NOT_EXPORTED) even though
209
- * the package is installed — that mislabeled installed sdks as missing.
210
- * On any resolve failure, fall back to walking node_modules by hand (the
211
- * same directory climb Node performs) and reading the entry from the sdk's
212
- * own package.json. Returns an absolute path, or null when truly absent.
213
- */
214
- const resolveSdkEntry = (repoDir) => {
215
- try {
216
- const repoRequire = createRequire(join(repoDir, "package.json"));
217
- return repoRequire.resolve("@lineadraw/sdk");
218
- } catch {
219
- for (let d = resolve(repoDir); ; d = dirname(d)) {
220
- const pkgPath = join(d, "node_modules", "@lineadraw", "sdk", "package.json");
221
- if (existsSync(pkgPath)) {
222
- const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
223
- const dot = pkg.exports?.["."];
224
- const entry =
225
- (typeof dot === "string" ? dot : (dot?.import ?? dot?.default)) ??
226
- pkg.main ??
227
- "./dist/index.js";
228
- return join(dirname(pkgPath), entry);
229
- }
230
- if (dirname(d) === d) return null;
231
- }
232
- }
233
- };
234
-
235
- export const verifyWithSdk = async (repoDir, manifest, sources) => {
236
- const entryPath = resolveSdkEntry(repoDir);
237
- if (entryPath === null)
238
- return { ran: false, problems: [], reason: "not-installed" };
206
+ export const verifyWithSdk = async (
207
+ manifest,
208
+ sources,
209
+ loadSdk = () => import("../dist/sdk.js"),
210
+ ) => {
239
211
  let sdk;
240
212
  try {
241
- sdk = await import(pathToFileURL(entryPath).href);
213
+ sdk = await loadSdk();
242
214
  } catch (e) {
243
- // Installed but not loadable (broken install, incompatible Node, …) —
244
- // must never be reported as "not installed".
245
215
  return {
246
216
  ran: false,
247
217
  problems: [],
@@ -249,8 +219,6 @@ export const verifyWithSdk = async (repoDir, manifest, sources) => {
249
219
  detail: e instanceof Error ? e.message : String(e),
250
220
  };
251
221
  }
252
- if (typeof sdk.testBlockDefinition !== "function")
253
- return { ran: false, problems: [], reason: "too-old" };
254
222
  const problems = [];
255
223
  // All block sources double as the resolution set for thumbnails, so a
256
224
  // block emitting instances of its catalog siblings previews complete.
@@ -265,17 +233,15 @@ export const verifyWithSdk = async (repoDir, manifest, sources) => {
265
233
  problems.push({ file: entry.source, problems: [result.error] });
266
234
  continue;
267
235
  }
268
- // Bake the preview thumbnail (sdk ≥ 0.12; older sdks still validate).
269
- // The render is deterministic, so the manifest stays byte-stable.
270
- if (typeof sdk.renderBlockThumbnailSvg === "function") {
271
- try {
272
- const svg = await sdk.renderBlockThumbnailSvg(sources.get(entry.id), {
273
- definitions: blockSources,
274
- });
275
- if (typeof svg === "string") thumbnails.set(entry.id, svg);
276
- } catch {
277
- // A block that validates but can't render just ships no thumbnail.
278
- }
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.
279
245
  }
280
246
  }
281
247
  return { ran: true, problems, thumbnails };