@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/index.js CHANGED
@@ -3614,6 +3614,48 @@ function getExistingModuleDir(projectRoot, modType, modName) {
3614
3614
  function checkModuleExists(projectRoot, modType, modName) {
3615
3615
  return getExistingModuleDir(projectRoot, modType, modName) !== null;
3616
3616
  }
3617
+ function getAvailableLocalModules(projectRoot, modType) {
3618
+ const folder = modType === "shared" ? "shared" : "features";
3619
+ const dirs = [
3620
+ path2.join(projectRoot, "src", folder),
3621
+ path2.join(projectRoot, folder)
3622
+ ];
3623
+ const set = /* @__PURE__ */ new Set();
3624
+ for (const dir of dirs) {
3625
+ if (fs4.existsSync(dir)) {
3626
+ try {
3627
+ const entries = fs4.readdirSync(dir, { withFileTypes: true });
3628
+ for (const e of entries) {
3629
+ if (e.isDirectory() && !e.name.startsWith(".")) {
3630
+ set.add(e.name);
3631
+ }
3632
+ }
3633
+ } catch {
3634
+ }
3635
+ }
3636
+ }
3637
+ return Array.from(set).sort();
3638
+ }
3639
+ function parseMappingFlags(raw) {
3640
+ if (!raw) return {};
3641
+ if (typeof raw === "object" && !Array.isArray(raw)) return raw;
3642
+ const result = {};
3643
+ const entries = Array.isArray(raw) ? raw : [raw];
3644
+ for (const entry of entries) {
3645
+ if (typeof entry === "string") {
3646
+ const idx = entry.indexOf(":");
3647
+ if (idx !== -1) {
3648
+ let key = entry.slice(0, idx).trim();
3649
+ let val = entry.slice(idx + 1).trim();
3650
+ val = val.replace(/^@\/(shared|features)\//, "").replace(/^(src\/)?(shared|features)\//, "").replace(/^\/+|\/+$/g, "").trim();
3651
+ if (key && val) {
3652
+ result[key] = val;
3653
+ }
3654
+ }
3655
+ }
3656
+ }
3657
+ return result;
3658
+ }
3617
3659
  async function handleImport(typeArg, nameArg, flags = {}) {
3618
3660
  const projectRoot = findProjectRoot();
3619
3661
  let type = typeArg?.trim().toLowerCase();
@@ -3734,7 +3776,8 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3734
3776
  const { type: modType, name: registryName, manifest } = node;
3735
3777
  const folder = modType === "shared" ? "shared" : "features";
3736
3778
  if (key === rootKey) {
3737
- const programmaticAlias2 = flags.alias?.[registryName] || flags.alias?.[`${modType}/${registryName}`];
3779
+ const aliasMap2 = parseMappingFlags(flags.alias);
3780
+ const programmaticAlias2 = aliasMap2[registryName] || aliasMap2[`${modType}/${registryName}`];
3738
3781
  const targetName = programmaticAlias2 || registryName;
3739
3782
  if (targetName !== registryName) {
3740
3783
  globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${targetName}/`);
@@ -3749,7 +3792,36 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3749
3792
  });
3750
3793
  continue;
3751
3794
  }
3752
- const programmaticAlias = flags.alias?.[registryName] || flags.alias?.[`${modType}/${registryName}`];
3795
+ const aliasMap = parseMappingFlags(flags.alias);
3796
+ const pointToMap = {
3797
+ ...parseMappingFlags(flags.pointTo),
3798
+ ...parseMappingFlags(flags["point-to"]),
3799
+ ...parseMappingFlags(flags.useExisting),
3800
+ ...parseMappingFlags(flags["use-existing"])
3801
+ };
3802
+ const programmaticPointTo = pointToMap[registryName] || pointToMap[`${modType}/${registryName}`];
3803
+ if (programmaticPointTo) {
3804
+ if (!checkModuleExists(projectRoot, modType, programmaticPointTo)) {
3805
+ p14.log.error(
3806
+ pc14.red(
3807
+ `Module '${programmaticPointTo}' specified in --point-to does not exist in ${folder}.`
3808
+ )
3809
+ );
3810
+ process.exit(1);
3811
+ }
3812
+ globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticPointTo}/`);
3813
+ globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticPointTo}`);
3814
+ p14.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${programmaticPointTo}'`);
3815
+ resolutions.set(key, {
3816
+ type: modType,
3817
+ registryName,
3818
+ targetName: programmaticPointTo,
3819
+ action: "reuse",
3820
+ manifest
3821
+ });
3822
+ continue;
3823
+ }
3824
+ const programmaticAlias = aliasMap[registryName] || aliasMap[`${modType}/${registryName}`];
3753
3825
  if (programmaticAlias) {
3754
3826
  globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticAlias}/`);
3755
3827
  globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticAlias}`);
@@ -3823,6 +3895,10 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3823
3895
  value: "keep_existing",
3824
3896
  label: `Keep existing local version (Recommended)`
3825
3897
  },
3898
+ {
3899
+ value: "point_existing",
3900
+ label: `Point to another existing ${modType} module`
3901
+ },
3826
3902
  {
3827
3903
  value: "rename",
3828
3904
  label: "Import fresh copy under a new name (alias)"
@@ -3845,6 +3921,78 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3845
3921
  });
3846
3922
  continue;
3847
3923
  }
3924
+ if (choice === "point_existing" || choice === "point_to_existing") {
3925
+ const candidates = getAvailableLocalModules(projectRoot, modType).filter(
3926
+ (m) => m !== registryName
3927
+ );
3928
+ let chosenName;
3929
+ if (candidates.length > 0) {
3930
+ const selection = handleCancel(
3931
+ await importPrompts.select({
3932
+ message: `Select an existing ${modType} module to point to:`,
3933
+ options: [
3934
+ ...candidates.map((m) => ({
3935
+ value: m,
3936
+ label: m
3937
+ })),
3938
+ {
3939
+ value: "__custom__",
3940
+ label: "Enter name manually..."
3941
+ }
3942
+ ]
3943
+ })
3944
+ );
3945
+ if (selection === "__custom__") {
3946
+ chosenName = handleCancel(
3947
+ await importPrompts.text({
3948
+ message: `Enter the name of the existing ${modType} module:`,
3949
+ placeholder: `my${registryName}`,
3950
+ validate(val) {
3951
+ const v = val?.trim();
3952
+ if (!v) return "Name cannot be empty";
3953
+ if (v === registryName) {
3954
+ return `Name must be different from '${registryName}' (or select 'Keep existing local version')`;
3955
+ }
3956
+ if (!checkModuleExists(projectRoot, modType, v)) {
3957
+ return `'${v}' does not exist in ${folder}`;
3958
+ }
3959
+ }
3960
+ })
3961
+ ).trim();
3962
+ } else {
3963
+ chosenName = selection;
3964
+ }
3965
+ } else {
3966
+ chosenName = handleCancel(
3967
+ await importPrompts.text({
3968
+ message: `Enter the name of the existing ${modType} module:`,
3969
+ placeholder: `my${registryName}`,
3970
+ validate(val) {
3971
+ const v = val?.trim();
3972
+ if (!v) return "Name cannot be empty";
3973
+ if (v === registryName) {
3974
+ return `Name must be different from '${registryName}' (or select 'Keep existing local version')`;
3975
+ }
3976
+ if (!checkModuleExists(projectRoot, modType, v)) {
3977
+ return `'${v}' does not exist in ${folder}`;
3978
+ }
3979
+ }
3980
+ })
3981
+ ).trim();
3982
+ }
3983
+ chosenName = chosenName.replace(/^@\/(shared|features)\//, "").replace(/^(src\/)?(shared|features)\//, "").replace(/^\/+|\/+$/g, "").trim();
3984
+ globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${chosenName}/`);
3985
+ globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${chosenName}`);
3986
+ p14.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${chosenName}'`);
3987
+ resolutions.set(key, {
3988
+ type: modType,
3989
+ registryName,
3990
+ targetName: chosenName,
3991
+ action: "reuse",
3992
+ manifest
3993
+ });
3994
+ continue;
3995
+ }
3848
3996
  if (choice === "rename") {
3849
3997
  const newName = handleCancel(
3850
3998
  await importPrompts.text({
@@ -3972,7 +4120,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3972
4120
  // src/cli.ts
3973
4121
  function createCli() {
3974
4122
  const cli = cac("mason");
3975
- 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) => {
4123
+ 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) => {
3976
4124
  if (args && args.length > 0) {
3977
4125
  p14.log.error(
3978
4126
  pc14.red(
@@ -4067,7 +4215,7 @@ function createCli() {
4067
4215
  cli.command(
4068
4216
  "import [type] [name]",
4069
4217
  "Import pre-built shared modules or features from the Solid Stack Registry"
4070
- ).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 = {}) => {
4218
+ ).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 = {}) => {
4071
4219
  try {
4072
4220
  await handleImport(type, name, flags);
4073
4221
  } catch (err) {
@@ -4097,7 +4245,7 @@ function createCli() {
4097
4245
  });
4098
4246
  }
4099
4247
  cli.help();
4100
- cli.version("1.0.16");
4248
+ cli.version("1.0.17");
4101
4249
  return cli;
4102
4250
  }
4103
4251
  function handleCliError(err) {