framer-code-link 0.21.0-alpha.3 → 0.21.0-alpha.5

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.
Files changed (2) hide show
  1. package/dist/index.mjs +32 -24
  2. package/package.json +1 -2
package/dist/index.mjs CHANGED
@@ -16,7 +16,6 @@ import { createHash as createHash$1 } from "crypto";
16
16
  import { execSync } from "child_process";
17
17
  import fs$2 from "fs";
18
18
  import { setupTypeAcquisition } from "@typescript/ata";
19
- import { parseImports } from "parse-imports";
20
19
  import ts from "typescript";
21
20
  import { fileURLToPath } from "node:url";
22
21
  import chokidar from "chokidar";
@@ -1515,7 +1514,7 @@ var Installer = class {
1515
1514
  for (const file of files) {
1516
1515
  if (!file.content || JSON_EXTENSION_REGEX.test(file.name)) continue;
1517
1516
  try {
1518
- const imports = await extractNpmPackageNames(file.content);
1517
+ const imports = extractNpmPackageNames(file.content);
1519
1518
  for (const packageName of imports) packageNames.add(packageName);
1520
1519
  } catch (err) {
1521
1520
  debug(`Type installer failed to parse imports for ${file.name}`, err);
@@ -1602,7 +1601,7 @@ var Installer = class {
1602
1601
  pkg.dependencies = sortDependencyMap(dependencies);
1603
1602
  pkg.devDependencies = sortDependencyMap(devDependencies);
1604
1603
  await fs.writeFile(packagePath, JSON.stringify(pkg, null, 4));
1605
- status("Updated dependencies. Run your package manager to install them.");
1604
+ success("Updated dependencies. Run your package manager to install them.");
1606
1605
  debug(`Updated package.json dependency versions for ${uniquePackageNames.join(", ")}`);
1607
1606
  }
1608
1607
  /**
@@ -1797,16 +1796,15 @@ function getBasePackageName(packageName) {
1797
1796
  if (packageName.startsWith("@")) return parts.length >= 2 ? parts.slice(0, 2).join("/") : packageName;
1798
1797
  return parts[0] ?? packageName;
1799
1798
  }
1800
- async function extractNpmPackageNames(code) {
1801
- const imports = await parseImports(code);
1799
+ function extractNpmPackageNames(code) {
1800
+ const { importedFiles } = ts.preProcessFile(code, true, true);
1802
1801
  const seen = /* @__PURE__ */ new Set();
1803
- for (const imported of imports) {
1804
- const specifier = imported.moduleSpecifier;
1805
- if (specifier.type !== "package" || !specifier.isConstant || !specifier.value) continue;
1806
- seen.add(getBasePackageName(specifier.value));
1807
- }
1802
+ for (const { fileName } of importedFiles) if (isPackageSpecifier(fileName)) seen.add(getBasePackageName(fileName));
1808
1803
  return [...seen];
1809
1804
  }
1805
+ function isPackageSpecifier(specifier) {
1806
+ return !specifier.startsWith(".") && !specifier.startsWith("/") && !/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(specifier);
1807
+ }
1810
1808
  function sortDependencyMap(dependencies) {
1811
1809
  return Object.fromEntries(Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b)));
1812
1810
  }
@@ -1885,19 +1883,30 @@ function isPlainObject(value) {
1885
1883
  }
1886
1884
  /**
1887
1885
  * Reads package.json, migrates legacy top-level Code Link fields into `codeLink`,
1888
- * and persists when anything changed.
1886
+ * backfills any provided defaults for missing `codeLink` fields, and persists
1887
+ * when anything changed.
1889
1888
  */
1890
- async function readAndMigratePackageJson(packageJsonPath) {
1889
+ async function readAndMigratePackageJson(packageJsonPath, defaults) {
1891
1890
  try {
1892
1891
  const raw = await fs.readFile(packageJsonPath, "utf-8");
1893
1892
  const parsed = JSON.parse(raw);
1894
1893
  if (!isPlainObject(parsed)) return null;
1895
- if (!("shortProjectHash" in parsed || "framerProjectName" in parsed || "codeLinkNpmStrategy" in parsed)) return parsed;
1894
+ const hadLegacy = "shortProjectHash" in parsed || "framerProjectName" in parsed || "codeLinkNpmStrategy" in parsed;
1896
1895
  const existing = parsed.codeLink;
1897
1896
  const base = isPlainObject(existing) ? { ...existing } : {};
1898
1897
  if (parsed.shortProjectHash !== void 0 && base.shortProjectHash === void 0) base.shortProjectHash = parsed.shortProjectHash;
1899
1898
  if (parsed.framerProjectName !== void 0 && base.framerProjectName === void 0) base.framerProjectName = parsed.framerProjectName;
1900
1899
  if (parsed.codeLinkNpmStrategy !== void 0 && base.npmStrategy === void 0) base.npmStrategy = parsed.codeLinkNpmStrategy;
1900
+ let backfilled = false;
1901
+ if (defaults?.shortProjectHash !== void 0 && base.shortProjectHash === void 0) {
1902
+ base.shortProjectHash = defaults.shortProjectHash;
1903
+ backfilled = true;
1904
+ }
1905
+ if (defaults?.framerProjectName !== void 0 && base.framerProjectName === void 0) {
1906
+ base.framerProjectName = defaults.framerProjectName;
1907
+ backfilled = true;
1908
+ }
1909
+ if (!hadLegacy && !backfilled) return parsed;
1901
1910
  const next = { ...parsed };
1902
1911
  delete next.shortProjectHash;
1903
1912
  delete next.framerProjectName;
@@ -1936,10 +1945,16 @@ async function findOrCreateProjectDirectory(options) {
1936
1945
  }
1937
1946
  const cwd = baseDirectory ?? process.cwd();
1938
1947
  const existing = await findExistingProjectDirectory(cwd, projectHash);
1939
- if (existing) return {
1940
- directory: existing,
1941
- created: false
1942
- };
1948
+ if (existing) {
1949
+ await readAndMigratePackageJson(path.join(existing, "package.json"), {
1950
+ shortProjectHash: shortProjectHash(projectHash),
1951
+ framerProjectName: projectName
1952
+ });
1953
+ return {
1954
+ directory: existing,
1955
+ created: false
1956
+ };
1957
+ }
1943
1958
  if (!projectName) throw new Error("Failed to get Project name. Pass --name <project name>.");
1944
1959
  const directoryName = toDirectoryName(projectName);
1945
1960
  const pkgName = toPackageName(projectName);
@@ -3991,13 +4006,6 @@ function parseUnsupportedNpmMode(mode) {
3991
4006
  if (mode === "acquire-types" || mode === "package-manager") return mode;
3992
4007
  throw new InvalidArgumentError("unsupported npm mode must be 'acquire-types' or 'package-manager'");
3993
4008
  }
3994
- program.exitOverride((err) => {
3995
- if (err.code === "commander.missingArgument") {
3996
- console.error("Missing Project ID. Copy command via Code Link Plugin.");
3997
- process.exit(err.exitCode);
3998
- }
3999
- throw err;
4000
- });
4001
4009
  program.name("framer-code-link").description("Sync Framer code components to your local filesystem").version(version).argument("[projectHash]", "Framer Project ID Hash (auto-detected from package.json if omitted)").option("-n, --name <name>", "Project name (optional)").option("-d, --dir <directory>", "Explicit project directory").option("--once", "Exit after the initial sync completes").option("-v, --verbose", "Enable verbose logging").option("--log-level <level>", "Set log level (debug, info, warn, error)").option("--dangerously-auto-delete", "Automatically delete remote files without confirmation").addOption(new Option("--unsupported-npm [mode]", "Handle unsupported npm packages (default without mode: acquire-types; modes: acquire-types, package-manager)").argParser(parseUnsupportedNpmMode).preset("acquire-types")).action(async (projectHash, options) => {
4002
4010
  if (!projectHash) {
4003
4011
  const detected = await getProjectHashFromCwd();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "framer-code-link",
3
- "version": "0.21.0-alpha.3",
3
+ "version": "0.21.0-alpha.5",
4
4
  "description": "CLI tool for syncing Framer code components - controller-centric architecture",
5
5
  "main": "dist/index.mjs",
6
6
  "type": "module",
@@ -26,7 +26,6 @@
26
26
  "@typescript/ata": "^0.9.8",
27
27
  "chokidar": "^5.0.0",
28
28
  "commander": "^14.0.3",
29
- "parse-imports": "^3.0.0",
30
29
  "prettier": "^3.7.4",
31
30
  "typescript": "^5.9.3",
32
31
  "ws": "^8.18.3"