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.
- package/dist/utils/build.js +0 -2
- package/dist/utils/createAction.js +7 -3
- package/dist/utils/createRoute.d.ts +7 -3
- package/dist/utils/createRoute.js +61 -22
- package/dist/utils/dev.js +0 -2
- package/dist/utils/hook.d.ts +6 -0
- package/dist/utils/hook.js +79 -45
- package/dist/utils/resolveProjectImportPath.d.ts +1 -0
- package/dist/utils/resolveProjectImportPath.js +201 -0
- package/dist/utils/syncSharedArtifacts.js +3 -9
- package/package.json +1 -1
- package/src/utils/build.ts +0 -3
- package/src/utils/createAction.ts +8 -3
- package/src/utils/createRoute.ts +75 -27
- package/src/utils/dev.ts +0 -3
- package/src/utils/hook.ts +89 -38
- package/src/utils/resolveProjectImportPath.ts +256 -0
- package/src/utils/syncSharedArtifacts.ts +4 -10
- package/dist/utils/excludeGeneratedFiles.d.ts +0 -1
- package/dist/utils/excludeGeneratedFiles.js +0 -10
- package/dist/utils/writeVsCodeSetting.d.ts +0 -7
- package/dist/utils/writeVsCodeSetting.js +0 -22
- package/src/utils/excludeGeneratedFiles.ts +0 -10
- package/src/utils/writeVsCodeSetting.ts +0 -38
package/dist/utils/build.js
CHANGED
|
@@ -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:
|
|
11
|
+
path: actionPath,
|
|
8
12
|
content: `"use server"
|
|
9
13
|
|
|
10
|
-
import { createResponseFn } from "
|
|
14
|
+
import { createResponseFn } from "${createResponseFnImportPath}"
|
|
11
15
|
|
|
12
|
-
import { ${info.name} } from "
|
|
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
|
|
2
|
-
export interface
|
|
3
|
-
|
|
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 {
|
|
1
|
+
import { readdir } from "fs/promises";
|
|
2
2
|
import { join } from "path";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveProjectImportPath } from "./resolveProjectImportPath.js";
|
|
4
|
+
import { getSharedModuleInfo, isScriptModule, writeGeneratedFile } from "./sharedArtifact.js";
|
|
4
5
|
async function createRoute(path) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
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:
|
|
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 {
|
|
47
|
+
import { createRouteFn, OriginalResponseFn, RouteBodyType, RouteHandler } from "${createRouteFnImportPath}"
|
|
48
|
+
${importLines ? `\n${importLines}\n` : ""}
|
|
49
|
+
const routeMap = new Map<string, RouteHandler>()
|
|
22
50
|
|
|
23
|
-
|
|
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
|
-
|
|
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));
|
package/dist/utils/hook.d.ts
CHANGED
|
@@ -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>;
|
package/dist/utils/hook.js
CHANGED
|
@@ -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
|
|
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 {
|
|
39
|
-
import { createRequestFn } from "deepsea-tools"
|
|
61
|
+
import { ${name}Action } from "${hookActionImportPath}"
|
|
40
62
|
|
|
41
|
-
import { ${
|
|
63
|
+
import { createUse${upName} } from "${hookPresetImportPath}"
|
|
42
64
|
|
|
43
65
|
export const ${name}Client = createRequestFn(${name}Action)
|
|
44
66
|
|
|
45
|
-
export
|
|
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
|
-
|
|
48
|
-
UseMutationOptions<Awaited<ReturnType<typeof ${name}Client>>, Error, ${clientInputType}, TOnMutateResult>,
|
|
49
|
-
"mutationFn"
|
|
50
|
-
> {}
|
|
73
|
+
import { ${name} } from "${mutationPresetSharedImportPath}"
|
|
51
74
|
|
|
52
|
-
export
|
|
75
|
+
export const createUse${upName} = withUseMutationDefaults<typeof ${name}>(() => {
|
|
53
76
|
const key = useId()
|
|
54
77
|
|
|
55
|
-
return
|
|
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
|
-
|
|
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 "
|
|
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 "
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
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)
|
|
186
|
-
|
|
187
|
-
|
|
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))
|
|
196
|
-
|
|
197
|
-
|
|
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
|
|
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
|
|
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
|
|
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
package/src/utils/build.ts
CHANGED
|
@@ -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
|