@thallylabs/migrate 0.2.44 → 0.2.46

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/index.d.ts CHANGED
@@ -118,7 +118,6 @@ declare function detectRepositoryPlatform(repositoryDir: string, docsDir?: strin
118
118
  declare function detectRepositoryDocsDir(repositoryDir: string): string;
119
119
  /** Enumerate generated endpoint routes using the same spec visibility, tab ids and slugs as migration linking. */
120
120
  declare function migrationApiRoutes(bundle: Pick<MigrationBundle, 'docsConfig' | 'assets'>): Array<string>;
121
- /** Import an already-available repository directory into a canonical bundle. */
122
121
  declare function migrateRepository(options: RepositoryMigrationOptions): MigrationBundle;
123
122
 
124
123
  /**
package/dist/index.js CHANGED
@@ -8217,7 +8217,122 @@ function propertyStart(source, name) {
8217
8217
  }
8218
8218
  return -1;
8219
8219
  }
8220
- function staticStringBindings(source) {
8220
+ function staticModulePath(repositoryRoot, specifier) {
8221
+ if (!specifier.startsWith(".")) return null;
8222
+ try {
8223
+ const relativePath = specifier.replace(/^\.\/+/, "");
8224
+ return ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".json"].map((extension) => withinRealRoot(resolveWithin(repositoryRoot, `${relativePath}${extension}`), repositoryRoot)).find((candidate) => existsSync4(candidate) && lstatSync4(candidate).isFile()) ?? null;
8225
+ } catch {
8226
+ return null;
8227
+ }
8228
+ }
8229
+ function staticObjectStrings(objectText) {
8230
+ const fields = /* @__PURE__ */ new Map();
8231
+ try {
8232
+ const parsed = JSON5.parse(objectText);
8233
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return fields;
8234
+ for (const [key, value] of Object.entries(parsed)) {
8235
+ if (typeof value === "string") fields.set(key, value);
8236
+ }
8237
+ } catch {
8238
+ return fields;
8239
+ }
8240
+ return fields;
8241
+ }
8242
+ function defaultExportObjectLiteralText(source) {
8243
+ const exported = /\bexport\s+default\s+/.exec(source);
8244
+ if (!exported) return null;
8245
+ const start = exported.index + exported[0].length;
8246
+ const literal = matchingObjectLiteral(source, start);
8247
+ if (literal) return literal;
8248
+ const identifier = source.slice(start).match(/^([A-Za-z_$][\w$]*)/)?.[1];
8249
+ if (!identifier) return null;
8250
+ const declaration = new RegExp(
8251
+ `\\b(?:const|let|var)\\s+${identifier}\\b(?:\\s*:\\s*[^=;]+)?\\s*=\\s*`
8252
+ ).exec(source);
8253
+ return declaration ? matchingObjectLiteral(source, declaration.index + declaration[0].length) : null;
8254
+ }
8255
+ function namedExportStringText(source, name) {
8256
+ const match = new RegExp(
8257
+ `\\bexport\\s+(?:const|let|var)\\s+${name}\\b(?:\\s*:\\s*[^=;]+)?\\s*=\\s*`
8258
+ ).exec(source);
8259
+ if (!match) return void 0;
8260
+ const quoted = source.slice(match.index + match[0].length).match(/^("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*')/);
8261
+ if (!quoted) return void 0;
8262
+ try {
8263
+ const parsed = JSON5.parse(quoted[1]);
8264
+ return typeof parsed === "string" ? parsed : void 0;
8265
+ } catch {
8266
+ return void 0;
8267
+ }
8268
+ }
8269
+ function staticImportBindings(source, repositoryRoot) {
8270
+ const bindings = /* @__PURE__ */ new Map();
8271
+ const addObject = (local, specifier, exported) => {
8272
+ const modulePath = staticModulePath(repositoryRoot, specifier);
8273
+ if (!modulePath) return;
8274
+ const moduleSource = readBoundedText(modulePath);
8275
+ const objectText = extname5(modulePath).toLowerCase() === ".json" ? moduleSource : exported === "default" ? defaultExportObjectLiteralText(moduleSource) : namedExportObjectLiteralText(moduleSource, exported);
8276
+ if (!objectText) return;
8277
+ for (const [key, value] of staticObjectStrings(objectText)) {
8278
+ bindings.set(`${local}.${key}`, value);
8279
+ }
8280
+ };
8281
+ const addString = (local, specifier, exported) => {
8282
+ const modulePath = staticModulePath(repositoryRoot, specifier);
8283
+ if (!modulePath || extname5(modulePath).toLowerCase() === ".json") return;
8284
+ const value = namedExportStringText(readBoundedText(modulePath), exported);
8285
+ if (value !== void 0) bindings.set(local, value);
8286
+ };
8287
+ const addNamespace = (local, specifier) => {
8288
+ const modulePath = staticModulePath(repositoryRoot, specifier);
8289
+ if (!modulePath) return;
8290
+ const moduleSource = readBoundedText(modulePath);
8291
+ if (extname5(modulePath).toLowerCase() === ".json") {
8292
+ for (const [key, value] of staticObjectStrings(moduleSource)) {
8293
+ bindings.set(`${local}.${key}`, value);
8294
+ }
8295
+ return;
8296
+ }
8297
+ const defaultText = defaultExportObjectLiteralText(moduleSource);
8298
+ if (defaultText) {
8299
+ for (const [key, value] of staticObjectStrings(defaultText)) {
8300
+ bindings.set(`${local}.default.${key}`, value);
8301
+ }
8302
+ }
8303
+ for (const match of moduleSource.matchAll(/\bexport\s+(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b/g)) {
8304
+ const name = match[1];
8305
+ const value = namedExportStringText(moduleSource, name);
8306
+ if (value !== void 0) bindings.set(`${local}.${name}`, value);
8307
+ const objectText = namedExportObjectLiteralText(moduleSource, name);
8308
+ if (!objectText) continue;
8309
+ for (const [key, objectValue5] of staticObjectStrings(objectText)) {
8310
+ bindings.set(`${local}.${name}.${key}`, objectValue5);
8311
+ }
8312
+ }
8313
+ };
8314
+ for (const match of source.matchAll(
8315
+ /\bimport\s+([A-Za-z_$][\w$]*)\s*(?:,\s*\{[^}]*\})?\s+from\s*(['"])([^'"]+)\2/g
8316
+ )) {
8317
+ addObject(match[1], match[3], "default");
8318
+ }
8319
+ for (const match of source.matchAll(
8320
+ /\bimport\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\s*(['"])([^'"]+)\2/g
8321
+ )) {
8322
+ addNamespace(match[1], match[3]);
8323
+ }
8324
+ for (const match of source.matchAll(/\bimport\s*\{([^}]*)\}\s*from\s*(['"])([^'"]+)\2/g)) {
8325
+ for (const entry of match[1].split(",")) {
8326
+ const [imported, alias] = entry.split(/\s+as\s+/).map((part) => part.trim());
8327
+ if (!imported || !/^[A-Za-z_$][\w$]*$/.test(imported)) continue;
8328
+ const local = alias && /^[A-Za-z_$][\w$]*$/.test(alias) ? alias : imported;
8329
+ addObject(local, match[3], imported);
8330
+ addString(local, match[3], imported);
8331
+ }
8332
+ }
8333
+ return bindings;
8334
+ }
8335
+ function staticStringBindings(source, repositoryRoot) {
8221
8336
  const bindings = /* @__PURE__ */ new Map();
8222
8337
  for (const match of source.matchAll(/\bconst\s+([A-Z][A-Z0-9_]*)\s*=\s*((?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'))/g)) {
8223
8338
  try {
@@ -8226,6 +8341,11 @@ function staticStringBindings(source) {
8226
8341
  } catch {
8227
8342
  }
8228
8343
  }
8344
+ if (repositoryRoot) {
8345
+ for (const [reference, value] of staticImportBindings(source, repositoryRoot)) {
8346
+ bindings.set(reference, value);
8347
+ }
8348
+ }
8229
8349
  return bindings;
8230
8350
  }
8231
8351
  function staticArrayField(source, name) {
@@ -8293,8 +8413,8 @@ function staticStringField(source, name, bindings) {
8293
8413
  if (start < 0) return void 0;
8294
8414
  const quoted = source.slice(start).match(/^("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*')/);
8295
8415
  if (!quoted) {
8296
- const binding = source.slice(start).match(/^([A-Z][A-Z0-9_]*)\b/)?.[1];
8297
- return binding ? bindings?.get(binding) : void 0;
8416
+ const reference = source.slice(start).match(/^([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)/)?.[1];
8417
+ return reference ? bindings?.get(reference) : void 0;
8298
8418
  }
8299
8419
  try {
8300
8420
  const parsed = JSON5.parse(quoted[1]);
@@ -8303,15 +8423,28 @@ function staticStringField(source, name, bindings) {
8303
8423
  return void 0;
8304
8424
  }
8305
8425
  }
8426
+ var COPYRIGHT_YEAR = /\$\{\s*new Date\(\)\s*\.\s*getFullYear\(\)\s*\}/g;
8427
+ var COPYRIGHT_TOKEN = /\$\{([^}]*)\}/g;
8428
+ var DOCUSAURUS_CREDIT = /\s*\bBuilt with Docusaurus\b[.!]*/gi;
8429
+ function docusaurusCopyright(source, bindings, unresolved) {
8430
+ const value = source.replace(COPYRIGHT_YEAR, "{year}").replace(COPYRIGHT_TOKEN, (_match, expression) => {
8431
+ const reference = expression.trim();
8432
+ const resolved = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(reference) ? bindings.get(reference) : void 0;
8433
+ if (resolved !== void 0) return resolved;
8434
+ unresolved.push(reference);
8435
+ return "";
8436
+ }).replace(DOCUSAURUS_CREDIT, "").replace(/[ \t]{2,}/g, " ").replace(/\s+([.,;!?])/g, "$1").trim();
8437
+ return value || void 0;
8438
+ }
8306
8439
  function readDocusaurusSiteSettings(repositoryRoot) {
8307
8440
  const empty2 = { docsRouteBasePath: "docs", defaultColorMode: "light", navbarLinks: [], footerLinks: [] };
8308
8441
  const configPath = findDocusaurusConfigPath(repositoryRoot);
8309
8442
  if (!configPath) return empty2;
8310
8443
  const source = readBoundedText(configPath);
8311
- const bindings = staticStringBindings(source);
8444
+ const bindings = staticStringBindings(source, repositoryRoot);
8312
8445
  const presets = staticArrayField(source, "presets");
8313
8446
  const classicDocs = presets && [...presets.matchAll(/["']?docs["']?\s*:\s*/g)].map((match) => matchingObjectLiteral(presets, (match.index ?? 0) + match[0].length)).find((object3) => Boolean(object3));
8314
- const docsRouteBasePath = classicDocs && staticStringField(classicDocs, "routeBasePath") || "docs";
8447
+ const docsRouteBasePath = classicDocs && staticStringField(classicDocs, "routeBasePath", bindings) || "docs";
8315
8448
  const themeStart = propertyStart(source, "themeConfig");
8316
8449
  const theme = themeStart < 0 ? null : matchingObjectLiteral(source, themeStart);
8317
8450
  const colorModeStart = theme ? propertyStart(theme, "colorMode") : -1;
@@ -8349,26 +8482,38 @@ function readDocusaurusSiteSettings(repositoryRoot) {
8349
8482
  });
8350
8483
  return items.length > 0 ? [{ heading, items }] : [];
8351
8484
  }) : [];
8352
- const copyright = footer && propertyStart(footer, "copyright") >= 0 ? staticStringField(footer, "copyright") ?? footer.slice(propertyStart(footer, "copyright")).match(/^`([^`]+)`/)?.[1]?.replace(/\$\{new Date\(\)\.getFullYear\(\)\}/g, "{year}") : void 0;
8485
+ const copyrightStart = footer ? propertyStart(footer, "copyright") : -1;
8486
+ const copyrightSource = copyrightStart < 0 ? void 0 : staticStringField(footer, "copyright", bindings) ?? footer.slice(copyrightStart).match(/^`([^`]+)`/)?.[1];
8487
+ const unresolvedCopyright = [];
8488
+ const copyright = copyrightSource === void 0 ? void 0 : docusaurusCopyright(copyrightSource, bindings, unresolvedCopyright);
8353
8489
  const announcementStart = theme ? propertyStart(theme, "announcementBar") : -1;
8354
8490
  const announcement = announcementStart < 0 ? null : matchingObjectLiteral(theme, announcementStart);
8355
8491
  const announcementContent = announcement && staticStringField(announcement, "content") || announcement?.slice(propertyStart(announcement, "content")).match(/^`([^`]+)`/)?.[1];
8356
8492
  const bannerContent = announcementContent ? announcementMarkdown(announcementContent) : "";
8493
+ const name = staticStringField(source, "title", bindings);
8494
+ const description = staticStringField(source, "tagline", bindings);
8495
+ const navbarTitle = navbar ? staticStringField(navbar, "title", bindings) : void 0;
8496
+ const unresolvedIdentity = [];
8497
+ if (!name && propertyStart(source, "title") >= 0) unresolvedIdentity.push("title");
8498
+ if (!description && propertyStart(source, "tagline") >= 0) unresolvedIdentity.push("tagline");
8499
+ if (!navbarTitle && navbar && propertyStart(navbar, "title") >= 0) unresolvedIdentity.push("navbarTitle");
8357
8500
  return {
8358
8501
  ...bannerContent ? { banner: { content: bannerContent, dismissible: !/isCloseable\s*:\s*false/.test(announcement), ...staticStringField(announcement, "id") ? { id: staticStringField(announcement, "id") } : {} } } : {},
8359
- name: staticStringField(source, "title"),
8360
- description: staticStringField(source, "tagline"),
8361
- ...navbar && staticStringField(navbar, "title") ? { navbarTitle: staticStringField(navbar, "title") } : {},
8502
+ ...name ? { name } : {},
8503
+ ...description ? { description } : {},
8504
+ ...navbarTitle ? { navbarTitle } : {},
8505
+ ...unresolvedIdentity.length > 0 ? { unresolvedIdentity } : {},
8506
+ ...unresolvedCopyright.length > 0 ? { unresolvedCopyright } : {},
8362
8507
  defaultColorMode,
8363
8508
  docsRouteBasePath,
8364
- ...logo && staticStringField(logo, "src") ? {
8509
+ ...logo && staticStringField(logo, "src", bindings) ? {
8365
8510
  logo: {
8366
- light: staticStringField(logo, "src"),
8367
- ...staticStringField(logo, "srcDark") ? { dark: staticStringField(logo, "srcDark") } : {},
8368
- showTitle: Boolean(navbar && staticStringField(navbar, "title"))
8511
+ light: staticStringField(logo, "src", bindings),
8512
+ ...staticStringField(logo, "srcDark", bindings) ? { dark: staticStringField(logo, "srcDark", bindings) } : {},
8513
+ showTitle: Boolean(navbarTitle)
8369
8514
  }
8370
8515
  } : {},
8371
- ...staticStringField(source, "favicon") ? { favicon: staticStringField(source, "favicon") } : {},
8516
+ ...staticStringField(source, "favicon", bindings) ? { favicon: staticStringField(source, "favicon", bindings) } : {},
8372
8517
  navbarLinks,
8373
8518
  footerLinks,
8374
8519
  ...copyright ? { copyright } : {}
@@ -15838,6 +15983,46 @@ function componentSourceIdentity(sourceUrl, repositoryDir, siteRoot, namespace)
15838
15983
  const docsRoot = relative6(repositoryDir, siteRoot).replace(/\\/g, "/");
15839
15984
  return JSON.stringify(namespace ? [repository, docsRoot, namespace] : [repository, docsRoot]);
15840
15985
  }
15986
+ function sourcePackageName(repositoryDir) {
15987
+ try {
15988
+ const path = withinRealRoot(resolveWithin(repositoryDir, "package.json"), repositoryDir);
15989
+ const metadata = lstatSync6(path);
15990
+ if (!metadata.isFile() || metadata.size > MAX_PAGE_BYTES) return void 0;
15991
+ const parsed = JSON.parse(readFileSync6(path, "utf8"));
15992
+ if (typeof parsed.name !== "string") return void 0;
15993
+ return parsed.name.split("/").pop()?.trim() || void 0;
15994
+ } catch {
15995
+ return void 0;
15996
+ }
15997
+ }
15998
+ function docusaurusProjectName(settings, repositoryDir, sourceUrl) {
15999
+ const configured = settings.navbarTitle ?? settings.name;
16000
+ if (configured) return { name: configured };
16001
+ const packageName = sourcePackageName(repositoryDir);
16002
+ if (packageName) return { name: packageName, origin: "the source package.json name" };
16003
+ const sourceName = (() => {
16004
+ try {
16005
+ const segment = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop();
16006
+ return segment ? decodeURIComponent(segment).replace(/\.git$/i, "") : void 0;
16007
+ } catch {
16008
+ return void 0;
16009
+ }
16010
+ })();
16011
+ if (sourceName) return { name: sourceName, origin: "the source repository name" };
16012
+ const sourceHost = (() => {
16013
+ try {
16014
+ return new URL(sourceUrl).hostname || void 0;
16015
+ } catch {
16016
+ return void 0;
16017
+ }
16018
+ })();
16019
+ return sourceHost ? { name: sourceHost, origin: "the source host" } : void 0;
16020
+ }
16021
+ var DOCUSAURUS_IDENTITY_LABELS = {
16022
+ title: "site title",
16023
+ tagline: "site tagline",
16024
+ navbarTitle: "navbar title"
16025
+ };
15841
16026
  function migrateRepository(options) {
15842
16027
  const repositoryDir = options.repositoryDir;
15843
16028
  const platform = options.platform ?? detectRepositoryPlatform(repositoryDir, options.docsDir);
@@ -17506,7 +17691,22 @@ ${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown
17506
17691
  const isTopLevelDocusaurus = platform === "docusaurus" && docusaurusProjectRoot && !options.docusaurusSkipRedirects;
17507
17692
  const docusaurusSettings = isTopLevelDocusaurus ? readDocusaurusSiteSettings(docusaurusProjectRoot) : void 0;
17508
17693
  const presentation = docusaurusSettings ? readDocusaurusPresentation(docusaurusProjectRoot) : void 0;
17694
+ const docusaurusIdentity = docusaurusSettings ? docusaurusProjectName(docusaurusSettings, repositoryDir, options.sourceUrl) : void 0;
17509
17695
  if (docusaurusSettings) {
17696
+ for (const field of docusaurusSettings.unresolvedIdentity ?? []) {
17697
+ const namesTheProject = field === "title" || field === "navbarTitle";
17698
+ const fallback = namesTheProject && docusaurusIdentity?.origin ? ` The project name was taken from ${docusaurusIdentity.origin}.` : "";
17699
+ warnings.push({
17700
+ code: "unsupported-config",
17701
+ message: `The Docusaurus ${DOCUSAURUS_IDENTITY_LABELS[field]} could not be read without running the config and was left out of the migration.${fallback} Use a string literal, or plain data in an imported module, to import it.`
17702
+ });
17703
+ }
17704
+ for (const token of docusaurusSettings.unresolvedCopyright ?? []) {
17705
+ warnings.push({
17706
+ code: "unsupported-config",
17707
+ message: `The Docusaurus footer copyright references ${token}, which could not be read without running the config, so the placeholder was removed. Use a literal value, or plain data in an imported module, to import it.`
17708
+ });
17709
+ }
17510
17710
  if (presentation) {
17511
17711
  assets.push({ path: "_thally/source-theme.css", content: Buffer.from(presentation.css) });
17512
17712
  docsConfig = { ...docsConfig, stylesheets: [...docsConfig.stylesheets ?? [], "/_thally/source-theme.css"], ...presentation.headingFont ? { fonts: { ...docsConfig.fonts, heading: { family: presentation.headingFont, weight: ["400", "500", "600", "700"] } } } : {} };
@@ -17753,9 +17953,9 @@ ${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown
17753
17953
  ...typeof fernRawConfig.title === "string" ? { name: fernRawConfig.title } : {},
17754
17954
  ...themeColors ? { colors: themeColors } : {}
17755
17955
  }
17756
- } : docusaurusSettings && (docusaurusSettings.name || docusaurusSettings.description || themeColors) ? {
17956
+ } : docusaurusSettings && (docusaurusIdentity?.name || docusaurusSettings.description || themeColors) ? {
17757
17957
  site: {
17758
- ...docusaurusSettings.navbarTitle || docusaurusSettings.name ? { name: docusaurusSettings.navbarTitle ?? docusaurusSettings.name } : {},
17958
+ ...docusaurusIdentity?.name ? { name: docusaurusIdentity.name } : {},
17759
17959
  ...docusaurusSettings.description ? { description: docusaurusSettings.description } : {},
17760
17960
  ...themeColors ? { colors: themeColors } : {}
17761
17961
  }