@zhin.js/command 1.0.3 → 1.0.4

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/lib/provider.js CHANGED
@@ -1,4 +1,4 @@
1
- import { basename, join, parse } from 'node:path';
1
+ import { basename, join, parse, sep } from 'node:path';
2
2
  import { featureId } from '@zhin.js/plugin-runtime';
3
3
  import { defineFeatureProvider, } from '@zhin.js/feature-kit';
4
4
  import { CommandIndex } from './command-index.js';
@@ -20,6 +20,23 @@ const commandFiles = {
20
20
  async function* discoverCommandDirectory(context, directory, ancestors) {
21
21
  const entries = [...await context.host.list(directory)]
22
22
  .sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
23
+ const files = entries.flatMap((entry) => {
24
+ if (entry.kind !== 'file')
25
+ return [];
26
+ const parsed = parseCommandFile(entry.name);
27
+ return parsed ? [{ entry, parsed }] : [];
28
+ });
29
+ const preferJavaScript = context.packageRoot
30
+ .split(sep)
31
+ .includes('node_modules');
32
+ const preferredFiles = new Map();
33
+ for (const { entry, parsed } of files) {
34
+ const current = preferredFiles.get(parsed.localSegment);
35
+ if (!current || commandFilePriority(entry.name, preferJavaScript)
36
+ < commandFilePriority(current, preferJavaScript)) {
37
+ preferredFiles.set(parsed.localSegment, entry.name);
38
+ }
39
+ }
23
40
  for (const entry of entries) {
24
41
  if (entry.kind === 'directory' && isCommandSegment(entry.name)) {
25
42
  yield* discoverCommandDirectory(context, join(directory, entry.name), [...ancestors, entry.name]);
@@ -30,6 +47,8 @@ async function* discoverCommandDirectory(context, directory, ancestors) {
30
47
  const file = parseCommandFile(entry.name);
31
48
  if (!file)
32
49
  continue;
50
+ if (preferredFiles.get(file.localSegment) !== entry.name)
51
+ continue;
33
52
  yield {
34
53
  localName: [...ancestors, file.localSegment].join('/'),
35
54
  source: join(directory, entry.name),
@@ -40,7 +59,7 @@ async function* discoverCommandDirectory(context, directory, ancestors) {
40
59
  function isCommandSegment(value) {
41
60
  return /^[a-z0-9][a-z0-9-]*$/.test(value);
42
61
  }
43
- const dynamicCommandFilePattern = /^\[([a-z][a-zA-Z0-9]*):([a-z][a-z0-9-]*)(?:=([^\]]*))?\]\.tsx?$/;
62
+ const dynamicCommandFilePattern = /^\[([a-z][a-zA-Z0-9]*):([a-z][a-z0-9-]*)(?:=([^\]]*))?\]\.(?:tsx?|[cm]?js)$/;
44
63
  const commandParameterTypes = new Set([
45
64
  'string',
46
65
  'number',
@@ -58,7 +77,7 @@ const commandParameterTypes = new Set([
58
77
  'rps',
59
78
  ]);
60
79
  function parseCommandFile(value) {
61
- if (/^[a-z0-9][a-z0-9-]*\.tsx?$/.test(value)) {
80
+ if (/^[a-z0-9][a-z0-9-]*\.(?:tsx?|[cm]?js)$/.test(value)) {
62
81
  return { localSegment: parse(value).name };
63
82
  }
64
83
  const match = dynamicCommandFilePattern.exec(value);
@@ -79,6 +98,14 @@ function parseCommandFile(value) {
79
98
  }
80
99
  return undefined;
81
100
  }
101
+ function commandFilePriority(value, preferJavaScript) {
102
+ const extension = value.slice(value.lastIndexOf('.') + 1);
103
+ const order = preferJavaScript
104
+ ? ['js', 'mjs', 'cjs', 'ts', 'tsx']
105
+ : ['ts', 'tsx', 'js', 'mjs', 'cjs'];
106
+ const priority = order.indexOf(extension);
107
+ return priority < 0 ? Number.MAX_SAFE_INTEGER : priority;
108
+ }
82
109
  function parseParameterValue(name, type, value, source) {
83
110
  if (type === 'string' || type === 'word' || type === 'text')
84
111
  return value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/command",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Convention-based Command Feature for Zhin Plugin Runtime",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -18,7 +18,7 @@
18
18
  ],
19
19
  "dependencies": {
20
20
  "segment-matcher": "^1.0.5",
21
- "@zhin.js/feature-kit": "1.0.3",
21
+ "@zhin.js/feature-kit": "1.0.4",
22
22
  "@zhin.js/plugin-runtime": "1.1.1"
23
23
  },
24
24
  "devDependencies": {
package/src/provider.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { basename, join, parse } from 'node:path';
1
+ import { basename, join, parse, sep } from 'node:path';
2
2
  import { featureId } from '@zhin.js/plugin-runtime';
3
3
  import {
4
4
  defineFeatureProvider,
@@ -38,6 +38,22 @@ async function* discoverCommandDirectory(
38
38
  ): AsyncIterable<DiscoveredSource> {
39
39
  const entries = [...await context.host.list(directory)]
40
40
  .sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
41
+ const files = entries.flatMap((entry) => {
42
+ if (entry.kind !== 'file') return [];
43
+ const parsed = parseCommandFile(entry.name);
44
+ return parsed ? [{ entry, parsed }] : [];
45
+ });
46
+ const preferJavaScript = context.packageRoot
47
+ .split(sep)
48
+ .includes('node_modules');
49
+ const preferredFiles = new Map<string, string>();
50
+ for (const { entry, parsed } of files) {
51
+ const current = preferredFiles.get(parsed.localSegment);
52
+ if (!current || commandFilePriority(entry.name, preferJavaScript)
53
+ < commandFilePriority(current, preferJavaScript)) {
54
+ preferredFiles.set(parsed.localSegment, entry.name);
55
+ }
56
+ }
41
57
  for (const entry of entries) {
42
58
  if (entry.kind === 'directory' && isCommandSegment(entry.name)) {
43
59
  yield* discoverCommandDirectory(
@@ -50,6 +66,7 @@ async function* discoverCommandDirectory(
50
66
  if (entry.kind !== 'file') continue;
51
67
  const file = parseCommandFile(entry.name);
52
68
  if (!file) continue;
69
+ if (preferredFiles.get(file.localSegment) !== entry.name) continue;
53
70
  yield {
54
71
  localName: [...ancestors, file.localSegment].join('/'),
55
72
  source: join(directory, entry.name),
@@ -68,7 +85,7 @@ interface ParsedCommandFile {
68
85
  }
69
86
 
70
87
  const dynamicCommandFilePattern =
71
- /^\[([a-z][a-zA-Z0-9]*):([a-z][a-z0-9-]*)(?:=([^\]]*))?\]\.tsx?$/;
88
+ /^\[([a-z][a-zA-Z0-9]*):([a-z][a-z0-9-]*)(?:=([^\]]*))?\]\.(?:tsx?|[cm]?js)$/;
72
89
 
73
90
  const commandParameterTypes = new Set<CommandParameterType>([
74
91
  'string',
@@ -88,7 +105,7 @@ const commandParameterTypes = new Set<CommandParameterType>([
88
105
  ]);
89
106
 
90
107
  function parseCommandFile(value: string): ParsedCommandFile | undefined {
91
- if (/^[a-z0-9][a-z0-9-]*\.tsx?$/.test(value)) {
108
+ if (/^[a-z0-9][a-z0-9-]*\.(?:tsx?|[cm]?js)$/.test(value)) {
92
109
  return { localSegment: parse(value).name };
93
110
  }
94
111
  const match = dynamicCommandFilePattern.exec(value);
@@ -110,6 +127,15 @@ function parseCommandFile(value: string): ParsedCommandFile | undefined {
110
127
  return undefined;
111
128
  }
112
129
 
130
+ function commandFilePriority(value: string, preferJavaScript: boolean): number {
131
+ const extension = value.slice(value.lastIndexOf('.') + 1);
132
+ const order = preferJavaScript
133
+ ? ['js', 'mjs', 'cjs', 'ts', 'tsx']
134
+ : ['ts', 'tsx', 'js', 'mjs', 'cjs'];
135
+ const priority = order.indexOf(extension);
136
+ return priority < 0 ? Number.MAX_SAFE_INTEGER : priority;
137
+ }
138
+
113
139
  function parseParameterValue(
114
140
  name: string,
115
141
  type: CommandParameterType,