akanjs 2.0.0-rc.7 → 2.0.0
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/base/primitiveRegistry.ts +28 -2
- package/cli/application/application.command.ts +11 -3
- package/cli/application/application.runner.ts +17 -1
- package/cli/guidelines/databaseModule/databaseModule.instruction.md +1 -1
- package/cli/guidelines/modelConstant/modelConstant.instruction.md +5 -5
- package/cli/guidelines/modelDocument/modelDocument.instruction.md +34 -61
- package/cli/guidelines/modelService/modelService.instruction.md +1 -1
- package/cli/index.js +9321 -19222
- package/cli/library/library.runner.ts +14 -13
- package/cli/package/package.runner.ts +31 -6
- package/cli/package/package.script.ts +2 -2
- package/cli/templates/app/page/_index.tsx +200 -79
- package/cli/templates/app/page/_layout.tsx +0 -1
- package/cli/templates/app/public/favicon.ico.template +0 -0
- package/cli/templates/app/public/logo.png.template +0 -0
- package/cli/templates/module/__Model__.Zone.tsx +1 -1
- package/cli/templates/module/__model__.document.ts +1 -1
- package/cli/templates/workspaceRoot/.gitignore.template +1 -11
- package/cli/templates/workspaceRoot/biome.json.template +16 -0
- package/cli/templates/workspaceRoot/package.json.template +1 -5
- package/cli/workspace/workspace.command.ts +7 -9
- package/cli/workspace/workspace.runner.ts +3 -13
- package/cli/workspace/workspace.script.ts +24 -9
- package/client/csrTypes.ts +1 -1
- package/constant/fieldInfo.ts +1 -1
- package/constant/serialize.ts +7 -1
- package/devkit/capacitor.base.config.ts +1 -1
- package/devkit/capacitorApp.ts +5 -1
- package/devkit/commandDecorators/argMeta.ts +28 -14
- package/devkit/commandDecorators/command.ts +41 -15
- package/devkit/commandDecorators/commandBuilder.ts +78 -42
- package/devkit/commandDecorators/helpFormatter.ts +7 -4
- package/devkit/dependencyScanner.ts +121 -15
- package/devkit/executors.ts +35 -23
- package/devkit/frontendBuild/cssCompiler.ts +9 -3
- package/devkit/incrementalBuilder/incrementalBuilder.proc.ts +2 -1
- package/devkit/lint/no-deep-internal-import.grit +25 -0
- package/devkit/lint/no-import-external-library.grit +1 -0
- package/devkit/mobile/mobileTarget.ts +48 -8
- package/devkit/scanInfo.ts +4 -1
- package/devkit/src/capacitorApp.ts +277 -0
- package/devkit/transforms/barrelImportsPlugin.ts +6 -0
- package/fetch/client/fetchClient.ts +1 -0
- package/fetch/client/httpClient.ts +13 -1
- package/package.json +37 -31
- package/server/akanServer.ts +21 -7
- package/server/hmr/clientScript.ts +8 -5
- package/server/resolver/resolver.contract.fixture.ts +1 -1
- package/test/index.ts +14 -0
- package/test/signalTest.preload.ts +10 -0
- package/test/signalTestRuntime.ts +126 -0
- package/test/testServer.ts +130 -25
- package/ui/Constant/Doc.tsx +696 -0
- package/ui/Constant/Mermaid.tsx +149 -0
- package/ui/Constant/index.ts +6 -0
- package/ui/Constant/schemaDoc.ts +324 -0
- package/ui/Field.tsx +0 -1
- package/ui/Portal.tsx +2 -0
- package/ui/System/CSR.tsx +6 -5
- package/ui/System/SSR.tsx +1 -1
- package/ui/System/SelectLanguage.tsx +1 -1
- package/ui/index.ts +1 -0
- package/ui/styles.css +0 -1
- package/webkit/bootCsr.tsx +8 -5
- package/base/test-globals.d.ts +0 -4
- package/cli/templates/app/common/commonLogic.ts +0 -12
- package/cli/templates/app/common/index.ts +0 -10
- package/cli/templates/app/public/favicon.ico +0 -0
- package/cli/templates/app/public/icons/icon-128x128.png +0 -0
- package/cli/templates/app/public/icons/icon-144x144.png +0 -0
- package/cli/templates/app/public/icons/icon-152x152.png +0 -0
- package/cli/templates/app/public/icons/icon-192x192.png +0 -0
- package/cli/templates/app/public/icons/icon-256x256.png +0 -0
- package/cli/templates/app/public/icons/icon-384x384.png +0 -0
- package/cli/templates/app/public/icons/icon-48x48.png +0 -0
- package/cli/templates/app/public/icons/icon-512x512.png +0 -0
- package/cli/templates/app/public/icons/icon-72x72.png +0 -0
- package/cli/templates/app/public/icons/icon-96x96.png +0 -0
- package/cli/templates/app/public/logo.svg +0 -70
- package/cli/templates/app/public/manifest.json.template +0 -67
- package/cli/templates/app/srvkit/backendLogic.ts +0 -12
- package/cli/templates/app/srvkit/index.ts +0 -10
- package/cli/templates/app/ui/UiComponent.ts +0 -16
- package/cli/templates/app/ui/index.ts +0 -10
- package/cli/templates/app/webkit/frontendLogic.ts +0 -12
- package/cli/templates/app/webkit/index.ts +0 -10
- package/cli/templates/module/index.tsx +0 -44
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
import { builtinModules } from "node:module";
|
|
1
2
|
import * as path from "node:path";
|
|
2
3
|
import ignore from "ignore";
|
|
4
|
+
import ts from "typescript";
|
|
3
5
|
import type { App, Lib, Pkg } from "./commandDecorators";
|
|
4
6
|
import { FileSys } from "./fileSys";
|
|
5
7
|
import type { PackageJson, TsConfigJson } from "./types";
|
|
6
8
|
|
|
9
|
+
const testFileRegex = /\.(?:test|spec)\.[cm]?[tj]sx?$/;
|
|
10
|
+
const builtinModuleSet = new Set([...builtinModules, ...builtinModules.map((mod) => `node:${mod}`)]);
|
|
11
|
+
const stripShebang = (source: string) => source.replace(/^#!.*(?:\r?\n|$)/, "");
|
|
12
|
+
|
|
7
13
|
export class TypeScriptDependencyScanner {
|
|
8
14
|
#fileDependencies = new Map<string, string[]>();
|
|
9
15
|
#fileRuntimeDependencies = new Map<string, string[]>();
|
|
@@ -57,6 +63,47 @@ export class TypeScriptDependencyScanner {
|
|
|
57
63
|
};
|
|
58
64
|
}
|
|
59
65
|
|
|
66
|
+
async getPackageBuildDependencies(
|
|
67
|
+
projectName: string,
|
|
68
|
+
): Promise<{ npmDeps: string[]; npmDevDeps: string[]; missingDeps: string[] }> {
|
|
69
|
+
const runtimeDeps = new Set<string>();
|
|
70
|
+
const devDeps = new Set<string>();
|
|
71
|
+
const sourceFiles = await this.#findTypeScriptFiles(this.directory, {
|
|
72
|
+
excludeBuildFiles: true,
|
|
73
|
+
excludeTestFiles: true,
|
|
74
|
+
});
|
|
75
|
+
const cssFiles = await this.#findCssFiles(this.directory);
|
|
76
|
+
|
|
77
|
+
for (const filePath of sourceFiles) {
|
|
78
|
+
const fileContent = await FileSys.readText(filePath);
|
|
79
|
+
const { imports, typeImports } = this.#extractImports(fileContent, filePath);
|
|
80
|
+
this.#addNormalizedImports(runtimeDeps, imports, projectName);
|
|
81
|
+
this.#addNormalizedImports(devDeps, typeImports, projectName);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (const filePath of cssFiles) {
|
|
85
|
+
const fileContent = await FileSys.readText(filePath);
|
|
86
|
+
this.#addNormalizedImports(runtimeDeps, this.#extractCssPluginImports(fileContent), projectName);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const buildFilePath = path.join(this.directory, "build.ts");
|
|
90
|
+
if (await FileSys.fileExists(buildFilePath)) {
|
|
91
|
+
const fileContent = await FileSys.readText(buildFilePath);
|
|
92
|
+
const { imports, typeImports } = this.#extractImports(fileContent, buildFilePath);
|
|
93
|
+
this.#addNormalizedImports(devDeps, [...imports, ...typeImports], projectName);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
for (const dep of runtimeDeps) devDeps.delete(dep);
|
|
97
|
+
|
|
98
|
+
const rootDeps = { ...this.rootPackageJson.dependencies, ...this.rootPackageJson.devDependencies };
|
|
99
|
+
const missingDeps = [...runtimeDeps, ...devDeps].filter((dep) => !rootDeps[dep]).sort();
|
|
100
|
+
return {
|
|
101
|
+
npmDeps: [...runtimeDeps].sort(),
|
|
102
|
+
npmDevDeps: [...devDeps].sort(),
|
|
103
|
+
missingDeps,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
60
107
|
async getImportSets<DepSets extends Set<string>[]>(depSets: DepSets): Promise<DepSets> {
|
|
61
108
|
const fileDependencies = await this.getDependencies();
|
|
62
109
|
return this.#getImportSetsFromDependencies(depSets, fileDependencies);
|
|
@@ -101,11 +148,35 @@ export class TypeScriptDependencyScanner {
|
|
|
101
148
|
return this.#fileDependencies;
|
|
102
149
|
}
|
|
103
150
|
|
|
104
|
-
async #findTypeScriptFiles(
|
|
151
|
+
async #findTypeScriptFiles(
|
|
152
|
+
directory: string,
|
|
153
|
+
{
|
|
154
|
+
excludeBuildFiles = false,
|
|
155
|
+
excludeTestFiles = false,
|
|
156
|
+
}: { excludeBuildFiles?: boolean; excludeTestFiles?: boolean } = {},
|
|
157
|
+
): Promise<string[]> {
|
|
105
158
|
const files: string[] = [];
|
|
106
159
|
const skipDirs = ["node_modules", "dist", "build", ".git", ".next", "public", "ios", "android"];
|
|
107
160
|
|
|
108
161
|
const glob = new Bun.Glob("**/*.{ts,tsx}");
|
|
162
|
+
for await (const filePath of glob.scan({ cwd: directory, onlyFiles: true })) {
|
|
163
|
+
if (skipDirs.some((dir) => filePath.includes(`/${dir}/`) || filePath.startsWith(`${dir}/`))) continue;
|
|
164
|
+
if (excludeBuildFiles && filePath === "build.ts") continue;
|
|
165
|
+
if (excludeTestFiles && testFileRegex.test(filePath)) continue;
|
|
166
|
+
|
|
167
|
+
const fullPath = path.join(directory, filePath);
|
|
168
|
+
const relativePath = path.relative(this.workspaceRoot, fullPath);
|
|
169
|
+
if (this.ig.ignores(relativePath)) continue;
|
|
170
|
+
|
|
171
|
+
files.push(fullPath);
|
|
172
|
+
}
|
|
173
|
+
return files;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async #findCssFiles(directory: string): Promise<string[]> {
|
|
177
|
+
const files: string[] = [];
|
|
178
|
+
const skipDirs = ["node_modules", "dist", "build", ".git", ".next", "public", "ios", "android"];
|
|
179
|
+
const glob = new Bun.Glob("**/*.css");
|
|
109
180
|
for await (const filePath of glob.scan({ cwd: directory, onlyFiles: true })) {
|
|
110
181
|
if (skipDirs.some((dir) => filePath.includes(`/${dir}/`) || filePath.startsWith(`${dir}/`))) continue;
|
|
111
182
|
|
|
@@ -160,27 +231,29 @@ export class TypeScriptDependencyScanner {
|
|
|
160
231
|
|
|
161
232
|
#extractImports(source: string, filePath: string) {
|
|
162
233
|
const transpiler = filePath.endsWith(".tsx") ? this.#tsxTranspiler : this.#tsTranspiler;
|
|
234
|
+
const scanSource = stripShebang(source);
|
|
163
235
|
const imports = new Set(
|
|
164
236
|
transpiler
|
|
165
|
-
.scanImports(
|
|
237
|
+
.scanImports(scanSource)
|
|
166
238
|
.map((imp) => imp.path)
|
|
167
239
|
.filter(Boolean),
|
|
168
240
|
);
|
|
169
241
|
const typeImports = new Set<string>();
|
|
170
|
-
const typeOnlyImportRegex = /\bimport\s+type\s+[\s\S]*?\s+from\s*["']([^"']+)["']/g;
|
|
171
|
-
for (const match of source.matchAll(typeOnlyImportRegex)) {
|
|
172
|
-
const importPath = match[1];
|
|
173
|
-
if (importPath && !imports.has(importPath)) typeImports.add(importPath);
|
|
174
|
-
}
|
|
175
242
|
|
|
176
|
-
const
|
|
177
|
-
for (const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
|
|
243
|
+
const sourceFile = ts.createSourceFile(filePath, scanSource, ts.ScriptTarget.Latest, true);
|
|
244
|
+
for (const statement of sourceFile.statements) {
|
|
245
|
+
if (!ts.isImportDeclaration(statement)) continue;
|
|
246
|
+
if (!ts.isStringLiteral(statement.moduleSpecifier)) continue;
|
|
247
|
+
|
|
248
|
+
const importPath = statement.moduleSpecifier.text;
|
|
249
|
+
const namedBindings = statement.importClause?.namedBindings;
|
|
250
|
+
const isNamedTypeOnlyImport =
|
|
251
|
+
namedBindings &&
|
|
252
|
+
ts.isNamedImports(namedBindings) &&
|
|
253
|
+
namedBindings.elements.length > 0 &&
|
|
254
|
+
namedBindings.elements.every((element) => element.isTypeOnly);
|
|
255
|
+
|
|
256
|
+
if ((statement.importClause?.isTypeOnly || isNamedTypeOnlyImport) && !imports.has(importPath)) {
|
|
184
257
|
typeImports.add(importPath);
|
|
185
258
|
}
|
|
186
259
|
}
|
|
@@ -188,6 +261,39 @@ export class TypeScriptDependencyScanner {
|
|
|
188
261
|
return { imports: [...imports], typeImports: [...typeImports] };
|
|
189
262
|
}
|
|
190
263
|
|
|
264
|
+
#extractCssPluginImports(source: string) {
|
|
265
|
+
const imports = new Set<string>();
|
|
266
|
+
const pluginRegex = /@plugin\s+(?:url\()?["']([^"')]+)["']\)?/g;
|
|
267
|
+
for (const match of source.matchAll(pluginRegex)) {
|
|
268
|
+
const importPath = match[1];
|
|
269
|
+
if (importPath) imports.add(importPath);
|
|
270
|
+
}
|
|
271
|
+
return [...imports];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
#addNormalizedImports(deps: Set<string>, imports: string[], projectName: string) {
|
|
275
|
+
for (const importPath of imports) {
|
|
276
|
+
const dep = this.#normalizePackageImport(importPath, projectName);
|
|
277
|
+
if (dep) deps.add(dep);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
#normalizePackageImport(importPath: string, projectName: string): string | null {
|
|
282
|
+
if (
|
|
283
|
+
importPath.startsWith(".") ||
|
|
284
|
+
importPath.startsWith("/") ||
|
|
285
|
+
importPath.startsWith("#") ||
|
|
286
|
+
importPath.startsWith("bun:") ||
|
|
287
|
+
builtinModuleSet.has(importPath)
|
|
288
|
+
)
|
|
289
|
+
return null;
|
|
290
|
+
|
|
291
|
+
const parts = importPath.split("/");
|
|
292
|
+
const packageName = importPath.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
293
|
+
if (!packageName || packageName === projectName || importPath.startsWith(`${projectName}/`)) return null;
|
|
294
|
+
return packageName;
|
|
295
|
+
}
|
|
296
|
+
|
|
191
297
|
generateDependencyGraph(): string {
|
|
192
298
|
let graph = "Dependency Graph:\n\n";
|
|
193
299
|
|
package/devkit/executors.ts
CHANGED
|
@@ -18,11 +18,10 @@ import {
|
|
|
18
18
|
validatePageSourceFile,
|
|
19
19
|
validateSubRoutePageKey,
|
|
20
20
|
} from "akanjs/common";
|
|
21
|
-
import chalk from "chalk";
|
|
22
|
-
import { AkanAppConfig, AkanLibConfig, decreaseBuildNum, increaseBuildNum } from "./akanConfig";
|
|
23
|
-
|
|
24
21
|
import { $ } from "bun";
|
|
22
|
+
import chalk from "chalk";
|
|
25
23
|
import ts from "typescript";
|
|
24
|
+
import { AkanAppConfig, AkanLibConfig, decreaseBuildNum, increaseBuildNum } from "./akanConfig";
|
|
26
25
|
import { FileSys } from "./fileSys";
|
|
27
26
|
import { getDirname } from "./getDirname";
|
|
28
27
|
import { Linter } from "./linter";
|
|
@@ -58,10 +57,7 @@ const parseEnvFile = (envPath: string): Record<string, string> => {
|
|
|
58
57
|
if (separatorIndex <= 0) continue;
|
|
59
58
|
const key = normalized.slice(0, separatorIndex).trim();
|
|
60
59
|
let value = normalized.slice(separatorIndex + 1).trim();
|
|
61
|
-
if (
|
|
62
|
-
(value.startsWith('"') && value.endsWith('"')) ||
|
|
63
|
-
(value.startsWith("'") && value.endsWith("'"))
|
|
64
|
-
) {
|
|
60
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
65
61
|
value = value.slice(1, -1);
|
|
66
62
|
}
|
|
67
63
|
env[key] = value;
|
|
@@ -1244,29 +1240,45 @@ export class PkgExecutor extends Executor {
|
|
|
1244
1240
|
this.#scanInfo = scanInfo;
|
|
1245
1241
|
return scanInfo;
|
|
1246
1242
|
}
|
|
1243
|
+
#toDependencyMap(
|
|
1244
|
+
rootPackageJson: PackageJson,
|
|
1245
|
+
dependencies: string[] = [],
|
|
1246
|
+
devDependencies: string[] = [],
|
|
1247
|
+
): Pick<PackageJson, "dependencies" | "devDependencies"> {
|
|
1248
|
+
const rootDeps = { ...rootPackageJson.dependencies, ...rootPackageJson.devDependencies };
|
|
1249
|
+
const dependencyNames = [...new Set(dependencies)].sort();
|
|
1250
|
+
const devDependencyNames = [...new Set(devDependencies)].filter((dep) => !dependencyNames.includes(dep)).sort();
|
|
1251
|
+
const missingDeps = [...dependencyNames, ...devDependencyNames].filter((dep) => !rootDeps[dep]).sort();
|
|
1252
|
+
if (missingDeps.length > 0)
|
|
1253
|
+
throw new Error(`Missing dependency versions in root package.json: ${missingDeps.join(", ")}`);
|
|
1254
|
+
|
|
1255
|
+
return {
|
|
1256
|
+
dependencies: Object.fromEntries(dependencyNames.map((dep) => [dep, rootDeps[dep]])),
|
|
1257
|
+
devDependencies: Object.fromEntries(devDependencyNames.map((dep) => [dep, rootDeps[dep]])),
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
async updatePackageJsonDependencies(
|
|
1261
|
+
dependencies: string[] = [],
|
|
1262
|
+
devDependencies: string[] = [],
|
|
1263
|
+
): Promise<PackageJson> {
|
|
1264
|
+
const [rootPackageJson, pkgJson] = await Promise.all([this.workspace.getPackageJson(), this.getPackageJson()]);
|
|
1265
|
+
const dependencyMaps = this.#toDependencyMap(rootPackageJson, dependencies, devDependencies);
|
|
1266
|
+
const newPkgJson = {
|
|
1267
|
+
...pkgJson,
|
|
1268
|
+
...dependencyMaps,
|
|
1269
|
+
};
|
|
1270
|
+
await this.writeJson("package.json", newPkgJson);
|
|
1271
|
+
return newPkgJson;
|
|
1272
|
+
}
|
|
1247
1273
|
async generateDistPackageJson(dependencies: string[] = [], devDependencies: string[] = []): Promise<PackageJson> {
|
|
1248
1274
|
const [rootPackageJson, pkgJson] = await Promise.all([this.workspace.getPackageJson(), this.getPackageJson()]);
|
|
1249
|
-
const
|
|
1250
|
-
const rootDeps = { ...rootDependencies, ...rootDevDependencies };
|
|
1275
|
+
const dependencyMaps = this.#toDependencyMap(rootPackageJson, dependencies, devDependencies);
|
|
1251
1276
|
const distPkgJson: PackageJson = {
|
|
1252
1277
|
...pkgJson,
|
|
1253
1278
|
type: "module",
|
|
1254
1279
|
exports: { ...pkgJson.exports, ".": { import: "./index.ts", types: "./index.ts", default: "./index.ts" } },
|
|
1255
1280
|
engines: { bun: ">=1.3.13" },
|
|
1256
|
-
|
|
1257
|
-
dependencies.map((dep) => {
|
|
1258
|
-
const rootVersion = rootDeps?.[dep];
|
|
1259
|
-
if (!rootVersion) this.logger.warn(`Package ${dep} is not found in root package.json`);
|
|
1260
|
-
return [dep, rootVersion];
|
|
1261
|
-
}),
|
|
1262
|
-
),
|
|
1263
|
-
devDependencies: Object.fromEntries(
|
|
1264
|
-
devDependencies.map((dep) => {
|
|
1265
|
-
const rootVersion = rootDeps?.[dep];
|
|
1266
|
-
if (!rootVersion) this.logger.warn(`Package ${dep} is not found in root package.json`);
|
|
1267
|
-
return [dep, rootVersion];
|
|
1268
|
-
}),
|
|
1269
|
-
),
|
|
1281
|
+
...dependencyMaps,
|
|
1270
1282
|
};
|
|
1271
1283
|
await Promise.all([this.dist.writeJson("package.json", distPkgJson), this.writeJson("package.json", distPkgJson)]);
|
|
1272
1284
|
return distPkgJson;
|
|
@@ -8,6 +8,8 @@ import { CssImportResolver } from "./cssImportResolver";
|
|
|
8
8
|
|
|
9
9
|
const SOURCE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"] as const;
|
|
10
10
|
const NON_SOURCE_EXT_RE = /\.(json|svg|png|jpe?g|webp|gif|avif|ico|woff2?|ttf|otf|mp3|mp4|wav)$/i;
|
|
11
|
+
const NODE_MODULES_RE = /[\\/]node_modules[\\/]/;
|
|
12
|
+
const AKANJS_NODE_MODULE_RE = /[\\/]node_modules[\\/]akanjs[\\/]/;
|
|
11
13
|
|
|
12
14
|
interface CssDiscovery {
|
|
13
15
|
cssPaths: string[];
|
|
@@ -88,7 +90,7 @@ export class CssCompiler {
|
|
|
88
90
|
|
|
89
91
|
while (queue.length > 0) {
|
|
90
92
|
const filePath = queue.shift();
|
|
91
|
-
if (!filePath || sourceFiles.has(filePath) || filePath
|
|
93
|
+
if (!filePath || sourceFiles.has(filePath) || isIgnoredNodeModuleSource(filePath)) continue;
|
|
92
94
|
sourceFiles.add(filePath);
|
|
93
95
|
|
|
94
96
|
let content: string;
|
|
@@ -125,7 +127,7 @@ export class CssCompiler {
|
|
|
125
127
|
}
|
|
126
128
|
if (NON_SOURCE_EXT_RE.test(spec)) continue;
|
|
127
129
|
const resolved = await this.#resolveSourceImport(spec, importerDir, resolvePackage);
|
|
128
|
-
if (!resolved || sourceFiles.has(resolved) || resolved
|
|
130
|
+
if (!resolved || sourceFiles.has(resolved) || isIgnoredNodeModuleSource(resolved)) continue;
|
|
129
131
|
queue.push(resolved);
|
|
130
132
|
}
|
|
131
133
|
}
|
|
@@ -221,7 +223,7 @@ export class CssCompiler {
|
|
|
221
223
|
await Promise.all(
|
|
222
224
|
dirs.map(async (dir) => {
|
|
223
225
|
for await (const file of glob.scan({ cwd: dir, absolute: true })) {
|
|
224
|
-
if (file
|
|
226
|
+
if (isIgnoredNodeModuleSource(file)) continue;
|
|
225
227
|
files.add(file);
|
|
226
228
|
}
|
|
227
229
|
}),
|
|
@@ -271,6 +273,10 @@ function isSourceFile(filePath: string) {
|
|
|
271
273
|
return SOURCE_EXTS.includes(path.extname(filePath) as (typeof SOURCE_EXTS)[number]);
|
|
272
274
|
}
|
|
273
275
|
|
|
276
|
+
export function isIgnoredNodeModuleSource(filePath: string): boolean {
|
|
277
|
+
return NODE_MODULES_RE.test(filePath) && !AKANJS_NODE_MODULE_RE.test(filePath);
|
|
278
|
+
}
|
|
279
|
+
|
|
274
280
|
function getPageKeyBasePath(pageKey: string, basePaths: string[]): string | null {
|
|
275
281
|
const normalized = pageKey.split(path.sep).join("/").replace(/^\.\//, "");
|
|
276
282
|
const segments = normalized.split("/");
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
engine biome(1.0)
|
|
2
|
+
language js(typescript, jsx)
|
|
3
|
+
|
|
4
|
+
or {
|
|
5
|
+
JsModuleSource() as $source where {
|
|
6
|
+
$source <: within JsImport(),
|
|
7
|
+
not $filename <: r".*\.(?:test|spec)\.tsx?",
|
|
8
|
+
$source <: r"\"@(?:apps|libs)/[^/]+/[^/]+/.+\"",
|
|
9
|
+
not $source <: r"\"@apps/[^/]+/env/env\.client\"",
|
|
10
|
+
register_diagnostic(
|
|
11
|
+
span = $source,
|
|
12
|
+
message = "@apps and @libs imports should only reference the first two path segments after the alias."
|
|
13
|
+
)
|
|
14
|
+
},
|
|
15
|
+
JsModuleSource() as $source where {
|
|
16
|
+
$source <: within JsImport(),
|
|
17
|
+
not $filename <: r".*\.(?:test|spec)\.tsx?",
|
|
18
|
+
$filename <: r".*apps/akasys/lib/projectBuild/[^/]+\.(?:constant|dictionary|document|service|signal|store)\.ts|.*apps/akasys/lib/projectBuild/[^/]+\.(?:Template|Unit|Util|View|Zone)\.tsx",
|
|
19
|
+
$source <: r"\"\.\./\.\./.*\"",
|
|
20
|
+
register_diagnostic(
|
|
21
|
+
span = $source,
|
|
22
|
+
message = "projectBuild module files should not import from two or more parent directories."
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -16,23 +16,63 @@ export const getMobileTargets = async (app: App): Promise<ResolvedMobileTarget[]
|
|
|
16
16
|
return Object.entries(config.mobile.targets).map(([name, target]) => ({ name, config: target }));
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
export const getMobileTargetChoices = async (app: App): Promise<string[]> => {
|
|
20
|
+
const config = await app.getConfig();
|
|
21
|
+
const targetNames = Object.keys(config.mobile.targets);
|
|
22
|
+
if (targetNames.length > 1) return targetNames;
|
|
23
|
+
const basePaths = [...config.basePaths];
|
|
24
|
+
if (basePaths.length > 1) return basePaths;
|
|
25
|
+
if (targetNames.length > 0) return targetNames;
|
|
26
|
+
return basePaths;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const resolveMobileTargetByBasePath = (
|
|
30
|
+
targets: ResolvedMobileTarget[],
|
|
31
|
+
basePath: string,
|
|
32
|
+
): ResolvedMobileTarget | undefined => {
|
|
33
|
+
const normalizedBasePath = basePath.replace(/^\/+|\/+$/g, "");
|
|
34
|
+
const byBasePath = targets.find((target) => target.config.basePath?.replace(/^\/+|\/+$/g, "") === normalizedBasePath);
|
|
35
|
+
if (byBasePath) return byBasePath;
|
|
36
|
+
const [template] = targets;
|
|
37
|
+
if (!template) return undefined;
|
|
38
|
+
return {
|
|
39
|
+
name: template.name,
|
|
40
|
+
config: {
|
|
41
|
+
...template.config,
|
|
42
|
+
basePath: normalizedBasePath,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
19
47
|
export const resolveMobileTargets = async (
|
|
20
48
|
app: App,
|
|
21
49
|
selection: MobileTargetSelection,
|
|
22
50
|
): Promise<ResolvedMobileTarget[]> => {
|
|
51
|
+
const config = await app.getConfig();
|
|
23
52
|
const targets = await getMobileTargets(app);
|
|
24
53
|
if (targets.length === 0) throw new Error(`No mobile targets configured for ${app.name}`);
|
|
25
54
|
if (!selection) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
55
|
+
const choices = await getMobileTargetChoices(app);
|
|
56
|
+
if (choices.length === 1) return resolveMobileTargets(app, choices[0]);
|
|
57
|
+
throw new Error(`Multiple mobile targets found for ${app.name}. Pass --target <${choices.join("|")}|all>.`);
|
|
58
|
+
}
|
|
59
|
+
if (selection === "all") {
|
|
60
|
+
if (Object.keys(config.mobile.targets).length > 1) return targets;
|
|
61
|
+
const basePaths = [...config.basePaths];
|
|
62
|
+
if (basePaths.length > 1) {
|
|
63
|
+
return basePaths.flatMap((basePath) => {
|
|
64
|
+
const resolved = resolveMobileTargetByBasePath(targets, basePath);
|
|
65
|
+
return resolved ? [resolved] : [];
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return targets;
|
|
30
69
|
}
|
|
31
|
-
if (selection === "all") return targets;
|
|
32
70
|
const target = targets.find((candidate) => candidate.name === selection);
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
return [
|
|
71
|
+
if (target) return [target];
|
|
72
|
+
const basePathTarget = resolveMobileTargetByBasePath(targets, selection);
|
|
73
|
+
if (basePathTarget && config.basePaths.has(selection.replace(/^\/+|\/+$/g, ""))) return [basePathTarget];
|
|
74
|
+
const choices = await getMobileTargetChoices(app);
|
|
75
|
+
throw new Error(`Mobile target '${selection}' was not found. Available: ${choices.join(", ")}`);
|
|
36
76
|
};
|
|
37
77
|
|
|
38
78
|
export const resolveMobilePath = (target: AkanMobileTargetConfig, pathname: string) => {
|
package/devkit/scanInfo.ts
CHANGED
|
@@ -91,8 +91,11 @@ const moduleUiFileTypes = {
|
|
|
91
91
|
scalar: new Set(["Template", "Unit"]),
|
|
92
92
|
} satisfies Record<ModuleKind, Set<string>>;
|
|
93
93
|
const testFilePattern = /\.(test|spec)\.(ts|tsx)$/;
|
|
94
|
+
const rootSignalTestFilePattern = /^[A-Za-z][A-Za-z0-9_-]*\.signal\.(test|spec)\.(ts|tsx)$/;
|
|
94
95
|
|
|
95
96
|
const isAllowedTestFile = (filename: string) => testFilePattern.test(filename);
|
|
97
|
+
const isAllowedLibRootFile = (filename: string) =>
|
|
98
|
+
libRootAllowedFiles.has(filename) || rootSignalTestFilePattern.test(filename);
|
|
96
99
|
const getScanPath = (exec: AppExecutor | LibExecutor, relativePath: string) =>
|
|
97
100
|
path.posix.join(`${exec.type}s`, exec.name, relativePath.split(path.sep).join("/"));
|
|
98
101
|
|
|
@@ -117,7 +120,7 @@ async function assertScanConvention(exec: AppExecutor | LibExecutor, libRoot: {
|
|
|
117
120
|
}
|
|
118
121
|
|
|
119
122
|
libRoot.files
|
|
120
|
-
.filter((filename) => !
|
|
123
|
+
.filter((filename) => !isAllowedLibRootFile(filename))
|
|
121
124
|
.forEach((filename) => {
|
|
122
125
|
addViolation(path.join("lib", filename), "unsupported lib root file");
|
|
123
126
|
});
|