@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.cjs CHANGED
@@ -3636,6 +3636,48 @@ function getExistingModuleDir(projectRoot, modType, modName) {
3636
3636
  function checkModuleExists(projectRoot, modType, modName) {
3637
3637
  return getExistingModuleDir(projectRoot, modType, modName) !== null;
3638
3638
  }
3639
+ function getAvailableLocalModules(projectRoot, modType) {
3640
+ const folder = modType === "shared" ? "shared" : "features";
3641
+ const dirs = [
3642
+ path2__default.default.join(projectRoot, "src", folder),
3643
+ path2__default.default.join(projectRoot, folder)
3644
+ ];
3645
+ const set = /* @__PURE__ */ new Set();
3646
+ for (const dir of dirs) {
3647
+ if (fs9__default.default.existsSync(dir)) {
3648
+ try {
3649
+ const entries = fs9__default.default.readdirSync(dir, { withFileTypes: true });
3650
+ for (const e of entries) {
3651
+ if (e.isDirectory() && !e.name.startsWith(".")) {
3652
+ set.add(e.name);
3653
+ }
3654
+ }
3655
+ } catch {
3656
+ }
3657
+ }
3658
+ }
3659
+ return Array.from(set).sort();
3660
+ }
3661
+ function parseMappingFlags(raw) {
3662
+ if (!raw) return {};
3663
+ if (typeof raw === "object" && !Array.isArray(raw)) return raw;
3664
+ const result = {};
3665
+ const entries = Array.isArray(raw) ? raw : [raw];
3666
+ for (const entry of entries) {
3667
+ if (typeof entry === "string") {
3668
+ const idx = entry.indexOf(":");
3669
+ if (idx !== -1) {
3670
+ let key = entry.slice(0, idx).trim();
3671
+ let val = entry.slice(idx + 1).trim();
3672
+ val = val.replace(/^@\/(shared|features)\//, "").replace(/^(src\/)?(shared|features)\//, "").replace(/^\/+|\/+$/g, "").trim();
3673
+ if (key && val) {
3674
+ result[key] = val;
3675
+ }
3676
+ }
3677
+ }
3678
+ }
3679
+ return result;
3680
+ }
3639
3681
  async function handleImport(typeArg, nameArg, flags = {}) {
3640
3682
  const projectRoot = findProjectRoot();
3641
3683
  let type = typeArg?.trim().toLowerCase();
@@ -3756,7 +3798,8 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3756
3798
  const { type: modType, name: registryName, manifest } = node;
3757
3799
  const folder = modType === "shared" ? "shared" : "features";
3758
3800
  if (key === rootKey) {
3759
- const programmaticAlias2 = flags.alias?.[registryName] || flags.alias?.[`${modType}/${registryName}`];
3801
+ const aliasMap2 = parseMappingFlags(flags.alias);
3802
+ const programmaticAlias2 = aliasMap2[registryName] || aliasMap2[`${modType}/${registryName}`];
3760
3803
  const targetName = programmaticAlias2 || registryName;
3761
3804
  if (targetName !== registryName) {
3762
3805
  globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${targetName}/`);
@@ -3771,7 +3814,36 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3771
3814
  });
3772
3815
  continue;
3773
3816
  }
3774
- const programmaticAlias = flags.alias?.[registryName] || flags.alias?.[`${modType}/${registryName}`];
3817
+ const aliasMap = parseMappingFlags(flags.alias);
3818
+ const pointToMap = {
3819
+ ...parseMappingFlags(flags.pointTo),
3820
+ ...parseMappingFlags(flags["point-to"]),
3821
+ ...parseMappingFlags(flags.useExisting),
3822
+ ...parseMappingFlags(flags["use-existing"])
3823
+ };
3824
+ const programmaticPointTo = pointToMap[registryName] || pointToMap[`${modType}/${registryName}`];
3825
+ if (programmaticPointTo) {
3826
+ if (!checkModuleExists(projectRoot, modType, programmaticPointTo)) {
3827
+ p14__namespace.log.error(
3828
+ pc14__default.default.red(
3829
+ `Module '${programmaticPointTo}' specified in --point-to does not exist in ${folder}.`
3830
+ )
3831
+ );
3832
+ process.exit(1);
3833
+ }
3834
+ globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticPointTo}/`);
3835
+ globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticPointTo}`);
3836
+ p14__namespace.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${programmaticPointTo}'`);
3837
+ resolutions.set(key, {
3838
+ type: modType,
3839
+ registryName,
3840
+ targetName: programmaticPointTo,
3841
+ action: "reuse",
3842
+ manifest
3843
+ });
3844
+ continue;
3845
+ }
3846
+ const programmaticAlias = aliasMap[registryName] || aliasMap[`${modType}/${registryName}`];
3775
3847
  if (programmaticAlias) {
3776
3848
  globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticAlias}/`);
3777
3849
  globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticAlias}`);
@@ -3845,6 +3917,10 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3845
3917
  value: "keep_existing",
3846
3918
  label: `Keep existing local version (Recommended)`
3847
3919
  },
3920
+ {
3921
+ value: "point_existing",
3922
+ label: `Point to another existing ${modType} module`
3923
+ },
3848
3924
  {
3849
3925
  value: "rename",
3850
3926
  label: "Import fresh copy under a new name (alias)"
@@ -3867,6 +3943,78 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3867
3943
  });
3868
3944
  continue;
3869
3945
  }
3946
+ if (choice === "point_existing" || choice === "point_to_existing") {
3947
+ const candidates = getAvailableLocalModules(projectRoot, modType).filter(
3948
+ (m) => m !== registryName
3949
+ );
3950
+ let chosenName;
3951
+ if (candidates.length > 0) {
3952
+ const selection = handleCancel(
3953
+ await importPrompts.select({
3954
+ message: `Select an existing ${modType} module to point to:`,
3955
+ options: [
3956
+ ...candidates.map((m) => ({
3957
+ value: m,
3958
+ label: m
3959
+ })),
3960
+ {
3961
+ value: "__custom__",
3962
+ label: "Enter name manually..."
3963
+ }
3964
+ ]
3965
+ })
3966
+ );
3967
+ if (selection === "__custom__") {
3968
+ chosenName = handleCancel(
3969
+ await importPrompts.text({
3970
+ message: `Enter the name of the existing ${modType} module:`,
3971
+ placeholder: `my${registryName}`,
3972
+ validate(val) {
3973
+ const v = val?.trim();
3974
+ if (!v) return "Name cannot be empty";
3975
+ if (v === registryName) {
3976
+ return `Name must be different from '${registryName}' (or select 'Keep existing local version')`;
3977
+ }
3978
+ if (!checkModuleExists(projectRoot, modType, v)) {
3979
+ return `'${v}' does not exist in ${folder}`;
3980
+ }
3981
+ }
3982
+ })
3983
+ ).trim();
3984
+ } else {
3985
+ chosenName = selection;
3986
+ }
3987
+ } else {
3988
+ chosenName = handleCancel(
3989
+ await importPrompts.text({
3990
+ message: `Enter the name of the existing ${modType} module:`,
3991
+ placeholder: `my${registryName}`,
3992
+ validate(val) {
3993
+ const v = val?.trim();
3994
+ if (!v) return "Name cannot be empty";
3995
+ if (v === registryName) {
3996
+ return `Name must be different from '${registryName}' (or select 'Keep existing local version')`;
3997
+ }
3998
+ if (!checkModuleExists(projectRoot, modType, v)) {
3999
+ return `'${v}' does not exist in ${folder}`;
4000
+ }
4001
+ }
4002
+ })
4003
+ ).trim();
4004
+ }
4005
+ chosenName = chosenName.replace(/^@\/(shared|features)\//, "").replace(/^(src\/)?(shared|features)\//, "").replace(/^\/+|\/+$/g, "").trim();
4006
+ globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${chosenName}/`);
4007
+ globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${chosenName}`);
4008
+ p14__namespace.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${chosenName}'`);
4009
+ resolutions.set(key, {
4010
+ type: modType,
4011
+ registryName,
4012
+ targetName: chosenName,
4013
+ action: "reuse",
4014
+ manifest
4015
+ });
4016
+ continue;
4017
+ }
3870
4018
  if (choice === "rename") {
3871
4019
  const newName = handleCancel(
3872
4020
  await importPrompts.text({
@@ -3994,7 +4142,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3994
4142
  // src/cli.ts
3995
4143
  function createCli() {
3996
4144
  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) => {
4145
+ 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
4146
  if (args && args.length > 0) {
3999
4147
  p14__namespace.log.error(
4000
4148
  pc14__default.default.red(
@@ -4089,7 +4237,7 @@ function createCli() {
4089
4237
  cli.command(
4090
4238
  "import [type] [name]",
4091
4239
  "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 = {}) => {
4240
+ ).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
4241
  try {
4094
4242
  await handleImport(type, name, flags);
4095
4243
  } catch (err) {
@@ -4119,7 +4267,7 @@ function createCli() {
4119
4267
  });
4120
4268
  }
4121
4269
  cli.help();
4122
- cli.version("1.0.16");
4270
+ cli.version("1.0.17");
4123
4271
  return cli;
4124
4272
  }
4125
4273
  function handleCliError(err) {