sdnext 0.0.25 → 0.0.27

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.
@@ -1,6 +1,5 @@
1
1
  import { readdir, stat } from "fs/promises";
2
2
  import { join } from "path";
3
- import { excludeGeneratedFiles } from "./excludeGeneratedFiles.js";
4
3
  import { runCommand } from "./runCommand.js";
5
4
  import { syncSharedArtifacts } from "./syncSharedArtifacts.js";
6
5
  async function buildFolder(dir) {
@@ -13,7 +12,6 @@ async function buildFolder(dir) {
13
12
  }
14
13
  }
15
14
  async function build(options, { args }) {
16
- await excludeGeneratedFiles();
17
15
  await buildFolder("shared");
18
16
  if (0 === args.length) return;
19
17
  process.exitCode = await runCommand({
@@ -1,15 +1,19 @@
1
1
  import { join } from "path";
2
+ import { resolveProjectImportPath } from "./resolveProjectImportPath.js";
2
3
  import { getSharedModuleInfo, isScriptModule, writeGeneratedFile } from "./sharedArtifact.js";
3
4
  async function createAction(path) {
4
5
  const info = getSharedModuleInfo(path);
5
6
  if (!isScriptModule(info.relativePath)) return;
7
+ const actionPath = join("actions", info.relativePath);
8
+ const createResponseFnImportPath = await resolveProjectImportPath(actionPath, "server/createResponseFn");
9
+ const sharedImportPath = await resolveProjectImportPath(actionPath, `shared/${info.importPath}`);
6
10
  await writeGeneratedFile({
7
- path: join("actions", info.relativePath),
11
+ path: actionPath,
8
12
  content: `"use server"
9
13
 
10
- import { createResponseFn } from "@/server/createResponseFn"
14
+ import { createResponseFn } from "${createResponseFnImportPath}"
11
15
 
12
- import { ${info.name} } from "@/shared/${info.importPath}"
16
+ import { ${info.name} } from "${sharedImportPath}"
13
17
 
14
18
  export const ${info.name}Action = createResponseFn(${info.name})
15
19
  `
@@ -1,5 +1,9 @@
1
- export declare function createRoute(path: string): Promise<void>;
2
- export interface IsRouteEnabledParams {
3
- content: string;
1
+ export declare function createRoute(path?: string): Promise<void>;
2
+ export interface SharedRouteModule {
3
+ importPath: string;
4
+ name: string;
5
+ }
6
+ export interface GetRouteFileContentParamsItem {
7
+ importPath: string;
4
8
  name: string;
5
9
  }
@@ -1,31 +1,70 @@
1
- import { readFile } from "fs/promises";
1
+ import { readdir } from "fs/promises";
2
2
  import { join } from "path";
3
- import { getSharedModuleInfo, isScriptModule, removeGeneratedFile, toKebabCase, writeGeneratedFile } from "./sharedArtifact.js";
3
+ import { resolveProjectImportPath } from "./resolveProjectImportPath.js";
4
+ import { getSharedModuleInfo, isScriptModule, writeGeneratedFile } from "./sharedArtifact.js";
4
5
  async function createRoute(path) {
5
- const info = getSharedModuleInfo(path);
6
- if (!isScriptModule(info.relativePath)) return;
7
- const routeDirPath = join("app", "api", "actions", info.dir, toKebabCase(info.name));
8
- const routePath = join(routeDirPath, "route.ts");
9
- const content = await readFile(join("shared", info.relativePath), "utf-8");
10
- if (!isRouteEnabled({
11
- content,
12
- name: info.name
13
- })) return void await removeGeneratedFile({
14
- path: routeDirPath,
15
- stopPath: join("app", "api", "actions")
16
- });
6
+ if (path) {
7
+ const info = getSharedModuleInfo(path);
8
+ if (!isScriptModule(info.relativePath)) return;
9
+ }
10
+ const modules = await getSharedModules("shared");
11
+ const routePath = join("app", "api", "action", "[...action]", "route.ts");
17
12
  await writeGeneratedFile({
18
13
  path: routePath,
19
- content: `import { createRoute } from "@/server/createResponseFn"
14
+ content: await getRouteFileContent(modules, routePath)
15
+ });
16
+ }
17
+ async function getSharedModules(dir) {
18
+ const entries = await readdir(dir, {
19
+ withFileTypes: true
20
+ });
21
+ const modules = [];
22
+ for (const entry of entries){
23
+ const itemPath = join(dir, entry.name);
24
+ if (entry.isDirectory()) {
25
+ modules.push(...await getSharedModules(itemPath));
26
+ continue;
27
+ }
28
+ if (!entry.isFile() || !isScriptModule(entry.name)) continue;
29
+ const info = getSharedModuleInfo(itemPath);
30
+ modules.push({
31
+ importPath: info.importPath,
32
+ name: info.name
33
+ });
34
+ }
35
+ modules.sort((a, b)=>a.importPath.localeCompare(b.importPath));
36
+ return modules;
37
+ }
38
+ async function getRouteFileContent(items, routePath) {
39
+ const createRouteFnImportPath = await resolveProjectImportPath(routePath, "server/createResponseFn");
40
+ const importLines = (await Promise.all(items.map(async (item)=>{
41
+ const importPath = await resolveProjectImportPath(routePath, `shared/${item.importPath}`);
42
+ return `import { ${item.name} } from "${importPath}"`;
43
+ }))).join("\n");
44
+ const registerLines = items.map((item)=>`registerRoute(${item.name})`).join("\n");
45
+ return `import { NextRequest, NextResponse } from "next/server"
20
46
 
21
- import { ${info.name} } from "@/shared/${info.importPath}"
47
+ import { createRouteFn, OriginalResponseFn, RouteBodyType, RouteHandler } from "${createRouteFnImportPath}"
48
+ ${importLines ? `\n${importLines}\n` : ""}
49
+ const routeMap = new Map<string, RouteHandler>()
22
50
 
23
- export const { POST } = createRoute(${info.name})
24
- `
25
- });
51
+ function registerRoute<TParams extends [arg?: unknown], TData, TPathname extends string, TRouteBodyType extends RouteBodyType = "json">(
52
+ fn: OriginalResponseFn<TParams, TData, TPathname, TRouteBodyType>,
53
+ ) {
54
+ if (!fn.route) return
55
+ const pathname = fn.route.pathname.replace(/(^\\/|\\/$)/g, "")
56
+ if (routeMap.has(pathname)) throw new Error(\`pathname \${pathname} is duplicate\`)
57
+ routeMap.set(pathname, createRouteFn(fn))
58
+ }
59
+
60
+ ${registerLines ? `${registerLines}\n\n` : ""}export function POST(request: NextRequest) {
61
+ const { pathname } = new URL(request.url)
62
+ const routeHandler = routeMap.get(pathname.replace(/(^\\/api\\/action\\/|\\/$)/g, ""))
63
+
64
+ if (!routeHandler) return NextResponse.json({ success: false, data: undefined, message: "Not Found", code: 404 }, { status: 404 })
65
+
66
+ return routeHandler(request)
26
67
  }
27
- function isRouteEnabled({ content, name }) {
28
- const routeRegExp = new RegExp(`\\b${name}\\.route\\s*=\\s*(true\\b|\\{)`);
29
- return routeRegExp.test(content);
68
+ `;
30
69
  }
31
70
  export { createRoute };
package/dist/utils/dev.js CHANGED
@@ -1,10 +1,8 @@
1
1
  import { spawn } from "child_process";
2
2
  import { fileURLToPath } from "url";
3
3
  import { buildFolder } from "./build.js";
4
- import { excludeGeneratedFiles } from "./excludeGeneratedFiles.js";
5
4
  import { spawnCommand } from "./runCommand.js";
6
5
  async function dev(options, { args }) {
7
- await excludeGeneratedFiles();
8
6
  await buildFolder("shared");
9
7
  if (0 === args.length) return;
10
8
  const watchPath = fileURLToPath(new URL("./watch.js", import.meta.url));
@@ -4,9 +4,15 @@ export type OperationType = HookType | "skip";
4
4
  export type HookContentMap = Record<HookType, string>;
5
5
  export interface HookData extends HookContentMap {
6
6
  hookPath: string;
7
+ mutationPreset: string;
8
+ mutationPresetPath: string;
7
9
  overwrite: boolean;
8
10
  type: HookType;
9
11
  }
12
+ export interface GeneratedFileState {
13
+ content: string;
14
+ overwrite: boolean;
15
+ }
10
16
  export declare function createHook(path: string, hookMap: Record<string, HookData>): Promise<void>;
11
17
  export declare function createHookFromFolder(): Promise<Record<string, HookData>>;
12
18
  export declare function hook(options: Record<string, string>, { args }: Command): Promise<void>;
@@ -3,6 +3,7 @@ import { join, parse, relative } from "path";
3
3
  import { cwd } from "process";
4
4
  import { checkbox as prompts_checkbox, select as prompts_select } from "@inquirer/prompts";
5
5
  import { readSdNextSetting } from "./readSdNextSetting.js";
6
+ import { resolveProjectImportPath } from "./resolveProjectImportPath.js";
6
7
  import { isScriptModule, normalizePathSeparator, writeGeneratedFile } from "./sharedArtifact.js";
7
8
  import { writeSdNextSetting } from "./writeSdNextSetting.js";
8
9
  function getHookTypeFromName(name) {
@@ -20,9 +21,24 @@ async function getHookTypeFromContent(path, content) {
20
21
  const type = setting.hook?.[path];
21
22
  if (void 0 !== type && "skip" !== type) return type;
22
23
  if (content.includes("useMutation")) return "mutation";
24
+ if (content.includes("createUse") && /from\s+["'][^"']*\/presets\//.test(content)) return "mutation";
23
25
  if (content.includes("ClientOptional")) return "get";
24
26
  if (content.includes("useQuery")) return "query";
25
27
  }
28
+ async function getGeneratedFileState(path) {
29
+ try {
30
+ const content = await readFile(path, "utf-8");
31
+ return {
32
+ content,
33
+ overwrite: !content.trim()
34
+ };
35
+ } catch (error) {
36
+ return {
37
+ content: "",
38
+ overwrite: true
39
+ };
40
+ }
41
+ }
26
42
  async function createHook(path, hookMap) {
27
43
  path = relative("actions", path).replace(/\\/g, "/");
28
44
  const { dir, name, base } = parse(path);
@@ -32,28 +48,34 @@ async function createHook(path, hookMap) {
32
48
  const actionImportPath = normalizePathSeparator(join(dir, name));
33
49
  const hookName = base.replace(/^./, (char)=>`use${char.toUpperCase()}`);
34
50
  const hookPath = join("hooks", dir, hookName);
51
+ const mutationPresetName = `createUse${upName}.ts`;
52
+ const mutationPresetPath = join("presets", dir, mutationPresetName);
53
+ const mutationPresetImportPath = normalizePathSeparator(join(dir, `createUse${upName}`));
35
54
  const clientInputType = `${upName}ClientInput`;
36
- const mutationHook = `import { useId } from "react"
55
+ const actionPath = normalizePathSeparator(join("actions", actionImportPath));
56
+ const hookActionImportPath = await resolveProjectImportPath(hookPath, actionPath);
57
+ const hookPresetImportPath = await resolveProjectImportPath(hookPath, normalizePathSeparator(join("presets", mutationPresetImportPath)));
58
+ const mutationPresetSharedImportPath = await resolveProjectImportPath(mutationPresetPath, normalizePathSeparator(join("shared", actionImportPath)));
59
+ const mutationHook = `import { createRequestFn } from "deepsea-tools"
37
60
 
38
- import { useMutation, UseMutationOptions } from "@tanstack/react-query"
39
- import { createRequestFn } from "deepsea-tools"
61
+ import { ${name}Action } from "${hookActionImportPath}"
40
62
 
41
- import { ${name}Action } from "@/actions/${actionImportPath}"
63
+ import { createUse${upName} } from "${hookPresetImportPath}"
42
64
 
43
65
  export const ${name}Client = createRequestFn(${name}Action)
44
66
 
45
- export type ${clientInputType} = Parameters<typeof ${name}Client> extends [] ? void : Parameters<typeof ${name}Client>[0]
67
+ export const use${upName} = createUse${upName}(${name}Client)
68
+ `;
69
+ const mutationPreset = `import { useId } from "react"
70
+
71
+ import { withUseMutationDefaults } from "soda-tanstack-query"
46
72
 
47
- export interface Use${upName}Params<TOnMutateResult = unknown> extends Omit<
48
- UseMutationOptions<Awaited<ReturnType<typeof ${name}Client>>, Error, ${clientInputType}, TOnMutateResult>,
49
- "mutationFn"
50
- > {}
73
+ import { ${name} } from "${mutationPresetSharedImportPath}"
51
74
 
52
- export function use${upName}<TOnMutateResult = unknown>({ onMutate, onSuccess, onError, onSettled, ...rest }: Use${upName}Params<TOnMutateResult> = {}) {
75
+ export const createUse${upName} = withUseMutationDefaults<typeof ${name}>(() => {
53
76
  const key = useId()
54
77
 
55
- return useMutation({
56
- mutationFn: ${name}Client,
78
+ return {
57
79
  onMutate(variables, context) {
58
80
  message.open({
59
81
  key,
@@ -61,8 +83,6 @@ export function use${upName}<TOnMutateResult = unknown>({ onMutate, onSuccess, o
61
83
  content: "中...",
62
84
  duration: 0,
63
85
  })
64
-
65
- return onMutate?.(variables, context) as TOnMutateResult | Promise<TOnMutateResult>
66
86
  },
67
87
  onSuccess(data, variables, onMutateResult, context) {
68
88
  context.client.invalidateQueries({ queryKey: ["query-${key.replace(/^.+?-/, "")}"] })
@@ -73,25 +93,18 @@ export function use${upName}<TOnMutateResult = unknown>({ onMutate, onSuccess, o
73
93
  type: "success",
74
94
  content: "成功",
75
95
  })
76
-
77
- return onSuccess?.(data, variables, onMutateResult, context)
78
96
  },
79
97
  onError(error, variables, onMutateResult, context) {
80
98
  message.destroy(key)
81
-
82
- return onError?.(error, variables, onMutateResult, context)
83
99
  },
84
- onSettled(data, error, variables, onMutateResult, context) {
85
- return onSettled?.(data, error, variables, onMutateResult, context)
86
- },
87
- ...rest,
88
- })
89
- }
100
+ onSettled(data, error, variables, onMutateResult, context) {},
101
+ }
102
+ })
90
103
  `;
91
104
  const getHook = `import { createRequestFn, isNonNullable } from "deepsea-tools"
92
105
  import { createUseQuery } from "soda-tanstack-query"
93
106
 
94
- import { ${name}Action } from "@/actions/${actionImportPath}"
107
+ import { ${name}Action } from "${hookActionImportPath}"
95
108
 
96
109
  export const ${name}Client = createRequestFn(${name}Action)
97
110
 
@@ -109,7 +122,7 @@ export const use${upName} = createUseQuery({
109
122
  const queryHook = `import { createRequestFn } from "deepsea-tools"
110
123
  import { createUseQuery } from "soda-tanstack-query"
111
124
 
112
- import { ${name}Action } from "@/actions/${actionImportPath}"
125
+ import { ${name}Action } from "${hookActionImportPath}"
113
126
 
114
127
  export const ${name}Client = createRequestFn(${name}Action)
115
128
 
@@ -124,19 +137,28 @@ export const use${upName} = createUseQuery({
124
137
  mutation: mutationHook
125
138
  };
126
139
  let hookType = getHookTypeFromName(name);
127
- let overwrite = true;
128
- try {
129
- const current = await readFile(hookPath, "utf-8");
130
- if (current.trim()) overwrite = false;
131
- const contentType = await getHookTypeFromContent(join(cwd(), hookPath), current);
132
- if (contentType) hookType = contentType;
133
- if (map[hookType] === current) return;
134
- } catch (error) {
135
- overwrite = true;
140
+ const hookState = await getGeneratedFileState(hookPath);
141
+ const contentType = await getHookTypeFromContent(join(cwd(), hookPath), hookState.content);
142
+ if (contentType) hookType = contentType;
143
+ if ("mutation" === hookType) {
144
+ const mutationPresetState = await getGeneratedFileState(mutationPresetPath);
145
+ if (map[hookType] === hookState.content && mutationPreset === mutationPresetState.content) return;
146
+ hookMap[path] = {
147
+ hookPath,
148
+ mutationPreset,
149
+ mutationPresetPath,
150
+ overwrite: hookState.overwrite && mutationPresetState.overwrite,
151
+ type: hookType,
152
+ ...map
153
+ };
154
+ return;
136
155
  }
156
+ if (map[hookType] === hookState.content) return;
137
157
  hookMap[path] = {
138
158
  hookPath,
139
- overwrite,
159
+ mutationPreset,
160
+ mutationPresetPath,
161
+ overwrite: hookState.overwrite,
140
162
  type: hookType,
141
163
  ...map
142
164
  };
@@ -168,7 +190,7 @@ async function hook(options, { args }) {
168
190
  const oldEntires = entires.filter(([path, { overwrite }])=>!overwrite);
169
191
  const root = cwd();
170
192
  const setting = await getSetting();
171
- for (const [path, { hookPath, overwrite, type, ...map }] of newEntires){
193
+ for (const [path, { hookPath, mutationPresetPath, mutationPreset, overwrite, type, ...map }] of newEntires){
172
194
  const resolved = join(root, hookPath);
173
195
  const answer = await prompts_select({
174
196
  message: path,
@@ -182,20 +204,32 @@ async function hook(options, { args }) {
182
204
  });
183
205
  setting.hook ??= {};
184
206
  setting.hook[resolved] = answer;
185
- if ("skip" !== answer) await writeGeneratedFile({
186
- path: hookPath,
187
- content: map[answer]
188
- });
207
+ if ("skip" !== answer) {
208
+ await writeGeneratedFile({
209
+ path: hookPath,
210
+ content: map[answer]
211
+ });
212
+ if ("mutation" === answer) await writeGeneratedFile({
213
+ path: mutationPresetPath,
214
+ content: mutationPreset
215
+ });
216
+ }
189
217
  }
190
218
  await writeSdNextSetting(setting);
191
219
  const overwrites = await prompts_checkbox({
192
220
  message: "Please check the hooks you want to overwrite",
193
221
  choices: oldEntires.map(([key])=>key)
194
222
  });
195
- for (const [path, { hookPath, overwrite, type, ...map }] of oldEntires)if (overwrites.includes(path)) await writeGeneratedFile({
196
- path: hookPath,
197
- content: map[type]
198
- });
223
+ for (const [path, { hookPath, mutationPresetPath, mutationPreset, overwrite, type, ...map }] of oldEntires)if (overwrites.includes(path)) {
224
+ await writeGeneratedFile({
225
+ path: hookPath,
226
+ content: map[type]
227
+ });
228
+ if ("mutation" === type) await writeGeneratedFile({
229
+ path: mutationPresetPath,
230
+ content: mutationPreset
231
+ });
232
+ }
199
233
  }
200
234
  function isNodeError(error) {
201
235
  return "object" == typeof error && null !== error;
@@ -0,0 +1 @@
1
+ export declare function resolveProjectImportPath(fromPath: string, targetPath: string): Promise<string>;
@@ -0,0 +1,201 @@
1
+ import { constants } from "node:fs";
2
+ import { access, readFile } from "node:fs/promises";
3
+ import { createRequire } from "node:module";
4
+ import { dirname, isAbsolute, join, normalize, relative, resolve } from "node:path";
5
+ import { cwd } from "node:process";
6
+ import { normalizePathSeparator } from "./sharedArtifact.js";
7
+ const resolveProjectImportPath_require = createRequire(import.meta.url);
8
+ const projectRoot = cwd();
9
+ let rootAliasPromise;
10
+ async function resolveProjectImportPath(fromPath, targetPath) {
11
+ const normalizedTargetPath = normalizePathSeparator(targetPath).replace(/^\.?\//, "");
12
+ const rootAlias = await getProjectRootAlias();
13
+ if (rootAlias) return `${rootAlias}/${normalizedTargetPath}`;
14
+ return getRelativeImportPath(fromPath, normalizedTargetPath);
15
+ }
16
+ function getRelativeImportPath(fromPath, targetPath) {
17
+ const fromDir = dirname(fromPath);
18
+ let importPath = normalizePathSeparator(relative(fromDir, targetPath));
19
+ if (!importPath.startsWith(".")) importPath = `./${importPath}`;
20
+ return importPath;
21
+ }
22
+ async function getProjectRootAlias() {
23
+ rootAliasPromise ??= resolveProjectRootAlias();
24
+ return rootAliasPromise;
25
+ }
26
+ async function resolveProjectRootAlias() {
27
+ const configPath = await findProjectConfigPath(projectRoot);
28
+ if (!configPath) return;
29
+ return readRootAliasFromConfig(configPath, new Set());
30
+ }
31
+ async function findProjectConfigPath(path) {
32
+ const candidates = [
33
+ join(path, "tsconfig.json"),
34
+ join(path, "jsconfig.json")
35
+ ];
36
+ for (const candidate of candidates)if (await exists(candidate)) return candidate;
37
+ }
38
+ async function readRootAliasFromConfig(configPath, seen) {
39
+ const normalizedPath = normalize(configPath);
40
+ if (seen.has(normalizedPath)) return;
41
+ seen.add(normalizedPath);
42
+ const config = await readJsonConfig(configPath);
43
+ const compilerOptions = toObject(config.compilerOptions);
44
+ const configDir = dirname(configPath);
45
+ const baseDir = getBaseDir(configDir, compilerOptions.baseUrl);
46
+ const paths = toPathMap(compilerOptions.paths);
47
+ if (paths) {
48
+ const rootAlias = findRootAlias(paths, baseDir, projectRoot);
49
+ if (rootAlias) return rootAlias;
50
+ }
51
+ const extendsValue = "string" == typeof config.extends ? config.extends : void 0;
52
+ if (!extendsValue) return;
53
+ const extendsPath = await resolveExtendsPath(extendsValue, configDir);
54
+ if (!extendsPath) return;
55
+ return readRootAliasFromConfig(extendsPath, seen);
56
+ }
57
+ function getBaseDir(configDir, baseUrl) {
58
+ if ("string" != typeof baseUrl) return configDir;
59
+ return resolve(configDir, baseUrl);
60
+ }
61
+ function findRootAlias(paths, baseDir, rootDir) {
62
+ for (const [key, targets] of Object.entries(paths)){
63
+ const alias = getAliasName(key);
64
+ if (alias) {
65
+ for (const target of targets)if (isRootTarget(target, baseDir, rootDir)) return alias;
66
+ }
67
+ }
68
+ }
69
+ function getAliasName(pattern) {
70
+ const wildcard = splitWildcard(pattern);
71
+ if (!wildcard || wildcard.suffix.length > 0) return;
72
+ const aliasPrefix = wildcard.prefix.replace(/\/$/, "");
73
+ if (!aliasPrefix) return;
74
+ return aliasPrefix;
75
+ }
76
+ function isRootTarget(pattern, baseDir, rootDir) {
77
+ const wildcard = splitWildcard(pattern);
78
+ if (!wildcard || wildcard.suffix.length > 0) return false;
79
+ const targetPath = resolve(baseDir, wildcard.prefix || ".");
80
+ return normalizeForComparison(targetPath) === normalizeForComparison(rootDir);
81
+ }
82
+ function splitWildcard(value) {
83
+ const index = value.indexOf("*");
84
+ if (index < 0) return;
85
+ if (value.indexOf("*", index + 1) >= 0) return;
86
+ return {
87
+ prefix: value.slice(0, index),
88
+ suffix: value.slice(index + 1)
89
+ };
90
+ }
91
+ async function resolveExtendsPath(extendsValue, baseDir) {
92
+ if (isAbsolute(extendsValue) || extendsValue.startsWith(".")) {
93
+ const relativeCandidates = getRelativeCandidates(resolve(baseDir, extendsValue));
94
+ for (const candidate of relativeCandidates)if (await exists(candidate)) return candidate;
95
+ return;
96
+ }
97
+ const moduleCandidates = getModuleCandidates(extendsValue);
98
+ for (const candidate of moduleCandidates)try {
99
+ return resolveProjectImportPath_require.resolve(candidate, {
100
+ paths: [
101
+ baseDir
102
+ ]
103
+ });
104
+ } catch (error) {}
105
+ }
106
+ function getRelativeCandidates(path) {
107
+ if (path.endsWith(".json")) return [
108
+ path
109
+ ];
110
+ return [
111
+ path,
112
+ `${path}.json`
113
+ ];
114
+ }
115
+ function getModuleCandidates(path) {
116
+ if (path.endsWith(".json")) return [
117
+ path
118
+ ];
119
+ return [
120
+ path,
121
+ `${path}.json`
122
+ ];
123
+ }
124
+ async function readJsonConfig(path) {
125
+ const content = await readFile(path, "utf-8");
126
+ const parsed = JSON.parse(stripTrailingCommas(stripComments(content)));
127
+ return toObject(parsed);
128
+ }
129
+ function stripComments(content) {
130
+ const output = [];
131
+ let inString = false;
132
+ let escaped = false;
133
+ for(let index = 0; index < content.length; index++){
134
+ const char = content[index];
135
+ const next = content[index + 1];
136
+ if (inString) {
137
+ output.push(char);
138
+ if (escaped) {
139
+ escaped = false;
140
+ continue;
141
+ }
142
+ if ("\\" === char) {
143
+ escaped = true;
144
+ continue;
145
+ }
146
+ if ("\"" === char) inString = false;
147
+ continue;
148
+ }
149
+ if ("\"" === char) {
150
+ inString = true;
151
+ output.push(char);
152
+ continue;
153
+ }
154
+ if ("/" === char && "/" === next) {
155
+ while(index < content.length && "\n" !== content[index])index++;
156
+ output.push("\n");
157
+ continue;
158
+ }
159
+ if ("/" === char && "*" === next) {
160
+ index += 2;
161
+ while(index < content.length && !("*" === content[index] && "/" === content[index + 1]))index++;
162
+ index++;
163
+ continue;
164
+ }
165
+ output.push(char);
166
+ }
167
+ return output.join("").replace(/^\uFEFF/, "");
168
+ }
169
+ function stripTrailingCommas(content) {
170
+ return content.replace(/,\s*([}\]])/g, "$1");
171
+ }
172
+ function toPathMap(value) {
173
+ if (!value || "object" != typeof value || Array.isArray(value)) return;
174
+ const entries = [];
175
+ for (const [key, item] of Object.entries(value)){
176
+ if (!Array.isArray(item)) continue;
177
+ const list = item.filter((target)=>"string" == typeof target);
178
+ if (0 !== list.length) entries.push([
179
+ key,
180
+ list
181
+ ]);
182
+ }
183
+ if (0 === entries.length) return;
184
+ return Object.fromEntries(entries);
185
+ }
186
+ function toObject(value) {
187
+ if (value && "object" == typeof value && !Array.isArray(value)) return value;
188
+ return {};
189
+ }
190
+ async function exists(path) {
191
+ try {
192
+ await access(path, constants.F_OK);
193
+ return true;
194
+ } catch (error) {
195
+ return false;
196
+ }
197
+ }
198
+ function normalizeForComparison(path) {
199
+ return normalize(path).replace(/\\/g, "/").replace(/\/$/, "").toLowerCase();
200
+ }
201
+ export { resolveProjectImportPath };
@@ -1,7 +1,7 @@
1
1
  import { join } from "path";
2
2
  import { createAction } from "./createAction.js";
3
3
  import { createRoute } from "./createRoute.js";
4
- import { getSharedModuleInfo, isScriptModule, normalizeSharedPath, removeGeneratedFile, toKebabCase } from "./sharedArtifact.js";
4
+ import { getSharedModuleInfo, isScriptModule, normalizeSharedPath, removeGeneratedFile } from "./sharedArtifact.js";
5
5
  async function syncSharedArtifacts(path) {
6
6
  const info = getSharedModuleInfo(path);
7
7
  if (!isScriptModule(info.relativePath)) return;
@@ -15,10 +15,7 @@ async function removeSharedArtifacts(path) {
15
15
  path: join("actions", info.relativePath),
16
16
  stopPath: "actions"
17
17
  });
18
- await removeGeneratedFile({
19
- path: join("app", "api", "actions", info.dir, toKebabCase(info.name)),
20
- stopPath: join("app", "api", "actions")
21
- });
18
+ await createRoute();
22
19
  }
23
20
  async function removeSharedArtifactDirectory(path) {
24
21
  const relativePath = normalizeSharedPath(path);
@@ -26,9 +23,6 @@ async function removeSharedArtifactDirectory(path) {
26
23
  path: join("actions", relativePath),
27
24
  stopPath: "actions"
28
25
  });
29
- await removeGeneratedFile({
30
- path: join("app", "api", "actions", relativePath),
31
- stopPath: join("app", "api", "actions")
32
- });
26
+ await createRoute();
33
27
  }
34
28
  export { removeSharedArtifactDirectory, removeSharedArtifacts, syncSharedArtifacts };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sdnext",
3
- "version": "0.0.25",
3
+ "version": "0.0.27",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "exports": {
@@ -3,7 +3,6 @@ import { join } from "path"
3
3
 
4
4
  import { Command } from "commander"
5
5
 
6
- import { excludeGeneratedFiles } from "./excludeGeneratedFiles"
7
6
  import { runCommand } from "./runCommand"
8
7
  import { syncSharedArtifacts } from "./syncSharedArtifacts"
9
8
 
@@ -20,8 +19,6 @@ export async function buildFolder(dir: string) {
20
19
  }
21
20
 
22
21
  export async function build(options: Record<string, string>, { args }: Command) {
23
- await excludeGeneratedFiles()
24
-
25
22
  await buildFolder("shared")
26
23
 
27
24
  if (args.length === 0) return