@solid-stack/mason 1.0.16 → 1.0.17

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.js CHANGED
@@ -3607,6 +3607,48 @@ function getExistingModuleDir(projectRoot, modType, modName) {
3607
3607
  function checkModuleExists(projectRoot, modType, modName) {
3608
3608
  return getExistingModuleDir(projectRoot, modType, modName) !== null;
3609
3609
  }
3610
+ function getAvailableLocalModules(projectRoot, modType) {
3611
+ const folder = modType === "shared" ? "shared" : "features";
3612
+ const dirs = [
3613
+ path2.join(projectRoot, "src", folder),
3614
+ path2.join(projectRoot, folder)
3615
+ ];
3616
+ const set = /* @__PURE__ */ new Set();
3617
+ for (const dir of dirs) {
3618
+ if (fs9.existsSync(dir)) {
3619
+ try {
3620
+ const entries = fs9.readdirSync(dir, { withFileTypes: true });
3621
+ for (const e of entries) {
3622
+ if (e.isDirectory() && !e.name.startsWith(".")) {
3623
+ set.add(e.name);
3624
+ }
3625
+ }
3626
+ } catch {
3627
+ }
3628
+ }
3629
+ }
3630
+ return Array.from(set).sort();
3631
+ }
3632
+ function parseMappingFlags(raw) {
3633
+ if (!raw) return {};
3634
+ if (typeof raw === "object" && !Array.isArray(raw)) return raw;
3635
+ const result = {};
3636
+ const entries = Array.isArray(raw) ? raw : [raw];
3637
+ for (const entry of entries) {
3638
+ if (typeof entry === "string") {
3639
+ const idx = entry.indexOf(":");
3640
+ if (idx !== -1) {
3641
+ let key = entry.slice(0, idx).trim();
3642
+ let val = entry.slice(idx + 1).trim();
3643
+ val = val.replace(/^@\/(shared|features)\//, "").replace(/^(src\/)?(shared|features)\//, "").replace(/^\/+|\/+$/g, "").trim();
3644
+ if (key && val) {
3645
+ result[key] = val;
3646
+ }
3647
+ }
3648
+ }
3649
+ }
3650
+ return result;
3651
+ }
3610
3652
  async function handleImport(typeArg, nameArg, flags = {}) {
3611
3653
  const projectRoot = findProjectRoot();
3612
3654
  let type = typeArg?.trim().toLowerCase();
@@ -3727,7 +3769,8 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3727
3769
  const { type: modType, name: registryName, manifest } = node;
3728
3770
  const folder = modType === "shared" ? "shared" : "features";
3729
3771
  if (key === rootKey) {
3730
- const programmaticAlias2 = flags.alias?.[registryName] || flags.alias?.[`${modType}/${registryName}`];
3772
+ const aliasMap2 = parseMappingFlags(flags.alias);
3773
+ const programmaticAlias2 = aliasMap2[registryName] || aliasMap2[`${modType}/${registryName}`];
3731
3774
  const targetName = programmaticAlias2 || registryName;
3732
3775
  if (targetName !== registryName) {
3733
3776
  globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${targetName}/`);
@@ -3742,7 +3785,36 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3742
3785
  });
3743
3786
  continue;
3744
3787
  }
3745
- const programmaticAlias = flags.alias?.[registryName] || flags.alias?.[`${modType}/${registryName}`];
3788
+ const aliasMap = parseMappingFlags(flags.alias);
3789
+ const pointToMap = {
3790
+ ...parseMappingFlags(flags.pointTo),
3791
+ ...parseMappingFlags(flags["point-to"]),
3792
+ ...parseMappingFlags(flags.useExisting),
3793
+ ...parseMappingFlags(flags["use-existing"])
3794
+ };
3795
+ const programmaticPointTo = pointToMap[registryName] || pointToMap[`${modType}/${registryName}`];
3796
+ if (programmaticPointTo) {
3797
+ if (!checkModuleExists(projectRoot, modType, programmaticPointTo)) {
3798
+ p14.log.error(
3799
+ pc14.red(
3800
+ `Module '${programmaticPointTo}' specified in --point-to does not exist in ${folder}.`
3801
+ )
3802
+ );
3803
+ process.exit(1);
3804
+ }
3805
+ globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticPointTo}/`);
3806
+ globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticPointTo}`);
3807
+ p14.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${programmaticPointTo}'`);
3808
+ resolutions.set(key, {
3809
+ type: modType,
3810
+ registryName,
3811
+ targetName: programmaticPointTo,
3812
+ action: "reuse",
3813
+ manifest
3814
+ });
3815
+ continue;
3816
+ }
3817
+ const programmaticAlias = aliasMap[registryName] || aliasMap[`${modType}/${registryName}`];
3746
3818
  if (programmaticAlias) {
3747
3819
  globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticAlias}/`);
3748
3820
  globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticAlias}`);
@@ -3816,6 +3888,10 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3816
3888
  value: "keep_existing",
3817
3889
  label: `Keep existing local version (Recommended)`
3818
3890
  },
3891
+ {
3892
+ value: "point_existing",
3893
+ label: `Point to another existing ${modType} module`
3894
+ },
3819
3895
  {
3820
3896
  value: "rename",
3821
3897
  label: "Import fresh copy under a new name (alias)"
@@ -3838,6 +3914,78 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3838
3914
  });
3839
3915
  continue;
3840
3916
  }
3917
+ if (choice === "point_existing" || choice === "point_to_existing") {
3918
+ const candidates = getAvailableLocalModules(projectRoot, modType).filter(
3919
+ (m) => m !== registryName
3920
+ );
3921
+ let chosenName;
3922
+ if (candidates.length > 0) {
3923
+ const selection = handleCancel(
3924
+ await importPrompts.select({
3925
+ message: `Select an existing ${modType} module to point to:`,
3926
+ options: [
3927
+ ...candidates.map((m) => ({
3928
+ value: m,
3929
+ label: m
3930
+ })),
3931
+ {
3932
+ value: "__custom__",
3933
+ label: "Enter name manually..."
3934
+ }
3935
+ ]
3936
+ })
3937
+ );
3938
+ if (selection === "__custom__") {
3939
+ chosenName = handleCancel(
3940
+ await importPrompts.text({
3941
+ message: `Enter the name of the existing ${modType} module:`,
3942
+ placeholder: `my${registryName}`,
3943
+ validate(val) {
3944
+ const v = val?.trim();
3945
+ if (!v) return "Name cannot be empty";
3946
+ if (v === registryName) {
3947
+ return `Name must be different from '${registryName}' (or select 'Keep existing local version')`;
3948
+ }
3949
+ if (!checkModuleExists(projectRoot, modType, v)) {
3950
+ return `'${v}' does not exist in ${folder}`;
3951
+ }
3952
+ }
3953
+ })
3954
+ ).trim();
3955
+ } else {
3956
+ chosenName = selection;
3957
+ }
3958
+ } else {
3959
+ chosenName = handleCancel(
3960
+ await importPrompts.text({
3961
+ message: `Enter the name of the existing ${modType} module:`,
3962
+ placeholder: `my${registryName}`,
3963
+ validate(val) {
3964
+ const v = val?.trim();
3965
+ if (!v) return "Name cannot be empty";
3966
+ if (v === registryName) {
3967
+ return `Name must be different from '${registryName}' (or select 'Keep existing local version')`;
3968
+ }
3969
+ if (!checkModuleExists(projectRoot, modType, v)) {
3970
+ return `'${v}' does not exist in ${folder}`;
3971
+ }
3972
+ }
3973
+ })
3974
+ ).trim();
3975
+ }
3976
+ chosenName = chosenName.replace(/^@\/(shared|features)\//, "").replace(/^(src\/)?(shared|features)\//, "").replace(/^\/+|\/+$/g, "").trim();
3977
+ globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${chosenName}/`);
3978
+ globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${chosenName}`);
3979
+ p14.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${chosenName}'`);
3980
+ resolutions.set(key, {
3981
+ type: modType,
3982
+ registryName,
3983
+ targetName: chosenName,
3984
+ action: "reuse",
3985
+ manifest
3986
+ });
3987
+ continue;
3988
+ }
3841
3989
  if (choice === "rename") {
3842
3990
  const newName = handleCancel(
3843
3991
  await importPrompts.text({
@@ -3965,7 +4113,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3965
4113
  // src/cli.ts
3966
4114
  function createCli() {
3967
4115
  const cli = cac("mason");
3968
- 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) => {
4116
+ 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) => {
3969
4117
  if (args && args.length > 0) {
3970
4118
  p14.log.error(
3971
4119
  pc14.red(
@@ -4060,7 +4208,7 @@ function createCli() {
4060
4208
  cli.command(
4061
4209
  "import [type] [name]",
4062
4210
  "Import pre-built shared modules or features from the Solid Stack Registry"
4063
- ).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 = {}) => {
4211
+ ).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 = {}) => {
4064
4212
  try {
4065
4213
  await handleImport(type, name, flags);
4066
4214
  } catch (err) {
@@ -4090,7 +4238,7 @@ function createCli() {
4090
4238
  });
4091
4239
  }
4092
4240
  cli.help();
4093
- cli.version("1.0.16");
4241
+ cli.version("1.0.17");
4094
4242
  return cli;
4095
4243
  }
4096
4244
  function handleCliError(err) {