@solid-stack/mason 1.0.16 → 1.0.18

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/bin.cjs CHANGED
@@ -175,7 +175,7 @@ function getEffectiveMode(projectRoot, flags) {
175
175
  if (flags?.registry) return "registry";
176
176
  const config = loadMasonConfig(projectRoot);
177
177
  if (config?.mode) return config.mode;
178
- if (fs9__default.default.existsSync(path2__default.default.join(projectRoot, "index.json")) && (fs9__default.default.existsSync(path2__default.default.join(projectRoot, "features")) || fs9__default.default.existsSync(path2__default.default.join(projectRoot, "shared"))) && !fs9__default.default.existsSync(path2__default.default.join(projectRoot, "src"))) {
178
+ if (fs9__default.default.existsSync(path2__default.default.join(projectRoot, "index.json")) && (fs9__default.default.existsSync(path2__default.default.join(projectRoot, "features")) || fs9__default.default.existsSync(path2__default.default.join(projectRoot, "shared")) || fs9__default.default.existsSync(path2__default.default.join(projectRoot, "src/features")) || fs9__default.default.existsSync(path2__default.default.join(projectRoot, "src/shared")))) {
179
179
  return "registry";
180
180
  }
181
181
  return "app";
@@ -189,6 +189,9 @@ function getConfiguredFeaturesDir(projectRoot, flags) {
189
189
  }
190
190
  const mode = getEffectiveMode(projectRoot, flags);
191
191
  if (mode === "registry") {
192
+ if (fs9__default.default.existsSync(path2__default.default.join(projectRoot, "src/features"))) {
193
+ return path2__default.default.join(projectRoot, "src/features");
194
+ }
192
195
  return path2__default.default.join(projectRoot, "features");
193
196
  }
194
197
  if (fs9__default.default.existsSync(path2__default.default.join(projectRoot, "src/features"))) {
@@ -205,6 +208,9 @@ function getConfiguredSharedDir(projectRoot, flags) {
205
208
  }
206
209
  const mode = getEffectiveMode(projectRoot, flags);
207
210
  if (mode === "registry") {
211
+ if (fs9__default.default.existsSync(path2__default.default.join(projectRoot, "src/shared"))) {
212
+ return path2__default.default.join(projectRoot, "src/shared");
213
+ }
208
214
  return path2__default.default.join(projectRoot, "shared");
209
215
  }
210
216
  if (fs9__default.default.existsSync(path2__default.default.join(projectRoot, "src/shared"))) {
@@ -3432,7 +3438,14 @@ async function fetchRegistryFile(registryBase, relPath) {
3432
3438
  if (token) {
3433
3439
  headers["Authorization"] = `Bearer ${token}`;
3434
3440
  }
3435
- const res = await fetch(url, { headers });
3441
+ let res = await fetch(url, { headers });
3442
+ if (!res.ok && res.status === 404 && !cleanRelPath.startsWith("src/")) {
3443
+ const srcUrl = `${registryBase.replace(/\/+$/, "")}/src/${cleanRelPath}`;
3444
+ const srcRes = await fetch(srcUrl, { headers });
3445
+ if (srcRes.ok) {
3446
+ res = srcRes;
3447
+ }
3448
+ }
3436
3449
  if (!res.ok) {
3437
3450
  let extraHint = "";
3438
3451
  if (res.status === 404) {
@@ -3452,9 +3465,14 @@ async function fetchRegistryFile(registryBase, relPath) {
3452
3465
  if (baseDir.startsWith("file://")) {
3453
3466
  baseDir = url.fileURLToPath(baseDir);
3454
3467
  }
3455
- const filePath = path2__default.default.resolve(baseDir, cleanRelPath);
3468
+ let filePath = path2__default.default.resolve(baseDir, cleanRelPath);
3456
3469
  if (!fs9__default.default.existsSync(filePath)) {
3457
- throw new Error(`Registry file not found at ${filePath}`);
3470
+ const inSrcPath = path2__default.default.resolve(baseDir, "src", cleanRelPath);
3471
+ if (fs9__default.default.existsSync(inSrcPath)) {
3472
+ filePath = inSrcPath;
3473
+ } else {
3474
+ throw new Error(`Registry file not found at ${filePath}`);
3475
+ }
3458
3476
  }
3459
3477
  return fs9__default.default.promises.readFile(filePath, "utf-8");
3460
3478
  }
@@ -3622,11 +3640,18 @@ function rewriteImports(content, aliasMap) {
3622
3640
  return rewritten;
3623
3641
  }
3624
3642
  function getModuleBaseDir(projectRoot, modType) {
3643
+ const mode = getEffectiveMode(projectRoot);
3644
+ if (mode === "registry") {
3645
+ return modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
3646
+ }
3625
3647
  const folder = modType === "shared" ? "shared" : "features";
3626
3648
  return path2__default.default.join(projectRoot, "src", folder);
3627
3649
  }
3628
3650
  function getExistingModuleDir(projectRoot, modType, modName) {
3629
3651
  const folder = modType === "shared" ? "shared" : "features";
3652
+ const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
3653
+ const inConfigured = path2__default.default.join(configuredDir, modName);
3654
+ if (fs9__default.default.existsSync(inConfigured)) return inConfigured;
3630
3655
  const inSrc = path2__default.default.join(projectRoot, "src", folder, modName);
3631
3656
  if (fs9__default.default.existsSync(inSrc)) return inSrc;
3632
3657
  const inRoot = path2__default.default.join(projectRoot, folder, modName);
@@ -3636,6 +3661,50 @@ function getExistingModuleDir(projectRoot, modType, modName) {
3636
3661
  function checkModuleExists(projectRoot, modType, modName) {
3637
3662
  return getExistingModuleDir(projectRoot, modType, modName) !== null;
3638
3663
  }
3664
+ function getAvailableLocalModules(projectRoot, modType) {
3665
+ const folder = modType === "shared" ? "shared" : "features";
3666
+ const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
3667
+ const dirs = [
3668
+ configuredDir,
3669
+ path2__default.default.join(projectRoot, "src", folder),
3670
+ path2__default.default.join(projectRoot, folder)
3671
+ ];
3672
+ const set = /* @__PURE__ */ new Set();
3673
+ for (const dir of dirs) {
3674
+ if (fs9__default.default.existsSync(dir)) {
3675
+ try {
3676
+ const entries = fs9__default.default.readdirSync(dir, { withFileTypes: true });
3677
+ for (const e of entries) {
3678
+ if (e.isDirectory() && !e.name.startsWith(".")) {
3679
+ set.add(e.name);
3680
+ }
3681
+ }
3682
+ } catch {
3683
+ }
3684
+ }
3685
+ }
3686
+ return Array.from(set).sort();
3687
+ }
3688
+ function parseMappingFlags(raw) {
3689
+ if (!raw) return {};
3690
+ if (typeof raw === "object" && !Array.isArray(raw)) return raw;
3691
+ const result = {};
3692
+ const entries = Array.isArray(raw) ? raw : [raw];
3693
+ for (const entry of entries) {
3694
+ if (typeof entry === "string") {
3695
+ const idx = entry.indexOf(":");
3696
+ if (idx !== -1) {
3697
+ let key = entry.slice(0, idx).trim();
3698
+ let val = entry.slice(idx + 1).trim();
3699
+ val = val.replace(/^@\/(shared|features)\//, "").replace(/^(src\/)?(shared|features)\//, "").replace(/^\/+|\/+$/g, "").trim();
3700
+ if (key && val) {
3701
+ result[key] = val;
3702
+ }
3703
+ }
3704
+ }
3705
+ }
3706
+ return result;
3707
+ }
3639
3708
  async function handleImport(typeArg, nameArg, flags = {}) {
3640
3709
  const projectRoot = findProjectRoot();
3641
3710
  let type = typeArg?.trim().toLowerCase();
@@ -3756,7 +3825,8 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3756
3825
  const { type: modType, name: registryName, manifest } = node;
3757
3826
  const folder = modType === "shared" ? "shared" : "features";
3758
3827
  if (key === rootKey) {
3759
- const programmaticAlias2 = flags.alias?.[registryName] || flags.alias?.[`${modType}/${registryName}`];
3828
+ const aliasMap2 = parseMappingFlags(flags.alias);
3829
+ const programmaticAlias2 = aliasMap2[registryName] || aliasMap2[`${modType}/${registryName}`];
3760
3830
  const targetName = programmaticAlias2 || registryName;
3761
3831
  if (targetName !== registryName) {
3762
3832
  globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${targetName}/`);
@@ -3771,7 +3841,36 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3771
3841
  });
3772
3842
  continue;
3773
3843
  }
3774
- const programmaticAlias = flags.alias?.[registryName] || flags.alias?.[`${modType}/${registryName}`];
3844
+ const aliasMap = parseMappingFlags(flags.alias);
3845
+ const pointToMap = {
3846
+ ...parseMappingFlags(flags.pointTo),
3847
+ ...parseMappingFlags(flags["point-to"]),
3848
+ ...parseMappingFlags(flags.useExisting),
3849
+ ...parseMappingFlags(flags["use-existing"])
3850
+ };
3851
+ const programmaticPointTo = pointToMap[registryName] || pointToMap[`${modType}/${registryName}`];
3852
+ if (programmaticPointTo) {
3853
+ if (!checkModuleExists(projectRoot, modType, programmaticPointTo)) {
3854
+ p14__namespace.log.error(
3855
+ pc14__default.default.red(
3856
+ `Module '${programmaticPointTo}' specified in --point-to does not exist in ${folder}.`
3857
+ )
3858
+ );
3859
+ process.exit(1);
3860
+ }
3861
+ globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticPointTo}/`);
3862
+ globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticPointTo}`);
3863
+ p14__namespace.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${programmaticPointTo}'`);
3864
+ resolutions.set(key, {
3865
+ type: modType,
3866
+ registryName,
3867
+ targetName: programmaticPointTo,
3868
+ action: "reuse",
3869
+ manifest
3870
+ });
3871
+ continue;
3872
+ }
3873
+ const programmaticAlias = aliasMap[registryName] || aliasMap[`${modType}/${registryName}`];
3775
3874
  if (programmaticAlias) {
3776
3875
  globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticAlias}/`);
3777
3876
  globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticAlias}`);
@@ -3845,6 +3944,10 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3845
3944
  value: "keep_existing",
3846
3945
  label: `Keep existing local version (Recommended)`
3847
3946
  },
3947
+ {
3948
+ value: "point_existing",
3949
+ label: `Point to another existing ${modType} module`
3950
+ },
3848
3951
  {
3849
3952
  value: "rename",
3850
3953
  label: "Import fresh copy under a new name (alias)"
@@ -3867,6 +3970,78 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3867
3970
  });
3868
3971
  continue;
3869
3972
  }
3973
+ if (choice === "point_existing" || choice === "point_to_existing") {
3974
+ const candidates = getAvailableLocalModules(projectRoot, modType).filter(
3975
+ (m) => m !== registryName
3976
+ );
3977
+ let chosenName;
3978
+ if (candidates.length > 0) {
3979
+ const selection = handleCancel(
3980
+ await importPrompts.select({
3981
+ message: `Select an existing ${modType} module to point to:`,
3982
+ options: [
3983
+ ...candidates.map((m) => ({
3984
+ value: m,
3985
+ label: m
3986
+ })),
3987
+ {
3988
+ value: "__custom__",
3989
+ label: "Enter name manually..."
3990
+ }
3991
+ ]
3992
+ })
3993
+ );
3994
+ if (selection === "__custom__") {
3995
+ chosenName = handleCancel(
3996
+ await importPrompts.text({
3997
+ message: `Enter the name of the existing ${modType} module:`,
3998
+ placeholder: `my${registryName}`,
3999
+ validate(val) {
4000
+ const v = val?.trim();
4001
+ if (!v) return "Name cannot be empty";
4002
+ if (v === registryName) {
4003
+ return `Name must be different from '${registryName}' (or select 'Keep existing local version')`;
4004
+ }
4005
+ if (!checkModuleExists(projectRoot, modType, v)) {
4006
+ return `'${v}' does not exist in ${folder}`;
4007
+ }
4008
+ }
4009
+ })
4010
+ ).trim();
4011
+ } else {
4012
+ chosenName = selection;
4013
+ }
4014
+ } else {
4015
+ chosenName = handleCancel(
4016
+ await importPrompts.text({
4017
+ message: `Enter the name of the existing ${modType} module:`,
4018
+ placeholder: `my${registryName}`,
4019
+ validate(val) {
4020
+ const v = val?.trim();
4021
+ if (!v) return "Name cannot be empty";
4022
+ if (v === registryName) {
4023
+ return `Name must be different from '${registryName}' (or select 'Keep existing local version')`;
4024
+ }
4025
+ if (!checkModuleExists(projectRoot, modType, v)) {
4026
+ return `'${v}' does not exist in ${folder}`;
4027
+ }
4028
+ }
4029
+ })
4030
+ ).trim();
4031
+ }
4032
+ chosenName = chosenName.replace(/^@\/(shared|features)\//, "").replace(/^(src\/)?(shared|features)\//, "").replace(/^\/+|\/+$/g, "").trim();
4033
+ globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${chosenName}/`);
4034
+ globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${chosenName}`);
4035
+ p14__namespace.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${chosenName}'`);
4036
+ resolutions.set(key, {
4037
+ type: modType,
4038
+ registryName,
4039
+ targetName: chosenName,
4040
+ action: "reuse",
4041
+ manifest
4042
+ });
4043
+ continue;
4044
+ }
3870
4045
  if (choice === "rename") {
3871
4046
  const newName = handleCancel(
3872
4047
  await importPrompts.text({
@@ -3994,7 +4169,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3994
4169
  // src/cli.ts
3995
4170
  function createCli() {
3996
4171
  const cli = cac.cac("mason");
3997
- cli.command("[...args]", "Interactive scaffolding and import wizard").option("-d, --dry-run", "Preview changes without writing to disk").option("-o, --overwrite", "Overwrite existing files").option("-y, --yes", "Accept defaults and skip interactive prompts").option("--registry", "Run in registry mode").action(async (args, flags) => {
4172
+ cli.command("[...args]", "Interactive scaffolding and import wizard").option("-d, --dry-run", "Preview changes without writing to disk").option("-o, --overwrite", "Overwrite existing files").option("-y, --yes", "Accept defaults and skip interactive prompts").option("--registry", "Run in registry mode").option("--point-to <mapping>", "Point a dependency to an existing local module (e.g. --point-to time:mytime)").option("--alias <mapping>", "Import a dependency fresh under an alias name (e.g. --alias time:time-mason)").action(async (args, flags) => {
3998
4173
  if (args && args.length > 0) {
3999
4174
  p14__namespace.log.error(
4000
4175
  pc14__default.default.red(
@@ -4089,7 +4264,7 @@ function createCli() {
4089
4264
  cli.command(
4090
4265
  "import [type] [name]",
4091
4266
  "Import pre-built shared modules or features from the Solid Stack Registry"
4092
- ).option("-o, --overwrite", "Overwrite existing files").option("-d, --dry-run", "Preview changes without writing to disk").option("-y, --yes", "Accept defaults and skip interactive prompts").option("--registry <url>", "Registry base URL or local directory").option("--skip-install", "Skip automatic package manager dependency installation").example("mason import shared hasher").example("mason import feature authn").example("mason import shared").example("mason import feature").action(async (type, name, flags = {}) => {
4267
+ ).option("-o, --overwrite", "Overwrite existing files").option("-d, --dry-run", "Preview changes without writing to disk").option("-y, --yes", "Accept defaults and skip interactive prompts").option("--registry <url>", "Registry base URL or local directory").option("--skip-install", "Skip automatic package manager dependency installation").option("--point-to <mapping>", "Point a dependency to an existing local module (e.g. --point-to time:mytime)").option("--alias <mapping>", "Import a dependency fresh under an alias name (e.g. --alias time:time-mason)").example("mason import shared hasher").example("mason import feature authn").example("mason import shared").example("mason import feature").action(async (type, name, flags = {}) => {
4093
4268
  try {
4094
4269
  await handleImport(type, name, flags);
4095
4270
  } catch (err) {
@@ -4119,7 +4294,7 @@ function createCli() {
4119
4294
  });
4120
4295
  }
4121
4296
  cli.help();
4122
- cli.version("1.0.16");
4297
+ cli.version("1.0.18");
4123
4298
  return cli;
4124
4299
  }
4125
4300
  function handleCliError(err) {