@akanjs/devkit 3.0.0-beta.9 → 3.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/DEV_RUNTIME_KNOBS.md +44 -26
- package/README.ko.md +21 -7
- package/README.md +17 -8
- package/abstractDoc.test.ts +6 -15
- package/abstractDoc.ts +6 -8
- package/agentsIndex.test.ts +4 -7
- package/agentsIndex.ts +6 -22
- package/akanApp/BackendImportGraph.test.ts +8 -22
- package/akanApp/BackendImportGraph.ts +1 -20
- package/akanApp/akanApp.host.test.ts +56 -3
- package/akanApp/akanApp.host.ts +218 -278
- package/akanApp/devBootLatch.test.ts +39 -0
- package/akanApp/devBootLatch.ts +39 -0
- package/akanApp/devHostPolicy.test.ts +81 -35
- package/akanApp/devHostPolicy.ts +41 -102
- package/akanConfig/akanBin.test.ts +62 -0
- package/akanConfig/akanBin.ts +78 -0
- package/akanConfig/akanConfig.test.ts +439 -141
- package/akanConfig/akanConfig.ts +359 -275
- package/akanConfig/index.ts +1 -0
- package/akanConfig/types.ts +22 -8
- package/akanContext.ts +110 -160
- package/akanMcpContract.ts +28 -73
- package/appSelectionMemory.ts +1 -4
- package/applicationBuildReporter.ts +37 -5
- package/applicationBuildRunner.test.ts +41 -5
- package/applicationBuildRunner.ts +71 -63
- package/applicationTestPreload.ts +3 -12
- package/artifact/implicitRootLayout.test.ts +32 -30
- package/artifact/implicitRootLayout.ts +58 -106
- package/artifact/routeSeedIndex.test.ts +7 -6
- package/artifact/routeSeedIndex.ts +11 -44
- package/biome.base.json +35 -2
- package/biomeBase.ts +2 -5
- package/cloud/cloudApi.test.ts +202 -0
- package/cloud/cloudApi.ts +77 -39
- package/cloud/configLock.test.ts +64 -0
- package/cloud/configLock.ts +96 -0
- package/cloud/constants.ts +26 -5
- package/cloud/globalConfig.ts +71 -32
- package/codeAgent/agent/AkanCodeServices.ts +97 -0
- package/codeAgent/agent/AkanContextFiles.ts +94 -0
- package/codeAgent/agent/CodeAgent.ts +551 -0
- package/codeAgent/agent/CodeAgentAsks.ts +66 -0
- package/codeAgent/agent/CodeAgentEventMapper.ts +225 -0
- package/codeAgent/agent/CodeAgentGate.ts +93 -0
- package/codeAgent/agent/CodeAgentProxy.ts +82 -0
- package/codeAgent/agent/CodeAgentRpcHost.ts +103 -0
- package/codeAgent/agent/CodeAgentRpcListener.ts +109 -0
- package/codeAgent/agent/CodeAgentStreamPrinter.ts +86 -0
- package/codeAgent/agent/CodeAgentSuspended.ts +85 -0
- package/codeAgent/agent/CodeAgentUi.ts +47 -0
- package/codeAgent/agent/CodeMailbox.ts +148 -0
- package/codeAgent/agent/CodeSessionFork.ts +56 -0
- package/codeAgent/agent/CodeSessionIndex.ts +78 -0
- package/codeAgent/agent/SubagentPool.ts +214 -0
- package/codeAgent/agent/akanCodeModel.ts +47 -0
- package/codeAgent/agent/akanCodePaths.ts +31 -0
- package/codeAgent/agent/akanContextFiles.test.ts +114 -0
- package/codeAgent/agent/akanSystemPrompt.ts +46 -0
- package/codeAgent/agent/codeAgent.test.ts +178 -0
- package/codeAgent/agent/codeAgentContract.test.ts +523 -0
- package/codeAgent/agent/codeAgentProxy.test.ts +74 -0
- package/codeAgent/agent/codeAgentRpcListener.test.ts +113 -0
- package/codeAgent/agent/codeAgentSuspended.test.ts +47 -0
- package/codeAgent/agent/codeMailbox.test.ts +76 -0
- package/codeAgent/agent/codeSessionFork.test.ts +58 -0
- package/codeAgent/agent/codeSessionIndex.test.ts +77 -0
- package/codeAgent/agent/consoleToStderr.ts +8 -0
- package/codeAgent/agent/jsonLines.ts +29 -0
- package/codeAgent/agent/stringArg.ts +5 -0
- package/codeAgent/feedback/DevLogFeedback.ts +82 -0
- package/codeAgent/feedback/PreviewChrome.ts +82 -0
- package/codeAgent/feedback/PreviewFeedback.ts +46 -0
- package/codeAgent/feedback/PreviewView.ts +95 -0
- package/codeAgent/feedback/TurnFeedback.ts +98 -0
- package/codeAgent/feedback/VerifyFeedback.ts +40 -0
- package/codeAgent/feedback/previewChrome.test.ts +73 -0
- package/codeAgent/index.ts +34 -0
- package/codeAgent/skills/akan-code/SKILL.md +130 -0
- package/codeAgent/skills/akan-debug/SKILL.md +42 -0
- package/codeAgent/skills/akan-deps/SKILL.md +50 -0
- package/codeAgent/skills/akan-err/SKILL.md +39 -0
- package/codeAgent/skills/akan-module/SKILL.md +45 -0
- package/codeAgent/skills/akan-scaffold/SKILL.md +76 -0
- package/codeAgent/skills/akan-scalar/SKILL.md +52 -0
- package/codeAgent/skills/akan-service/SKILL.md +34 -0
- package/codeAgent/skills/akan-store/SKILL.md +67 -0
- package/codeAgent/skills/akan-ui/SKILL.md +63 -0
- package/codeAgent/skills/akan-validate/SKILL.md +44 -0
- package/codeAgent/tools/AkanCli.ts +50 -0
- package/codeAgent/tools/AkanCodePlugins.ts +136 -0
- package/codeAgent/tools/AkanEditScope.ts +48 -0
- package/codeAgent/tools/AkanEnvKeys.ts +50 -0
- package/codeAgent/tools/AkanToolPack.ts +92 -0
- package/codeAgent/tools/AkanVerifier.ts +48 -0
- package/codeAgent/tools/AskToolPack.ts +99 -0
- package/codeAgent/tools/MailToolPack.ts +76 -0
- package/codeAgent/tools/McpClient.ts +187 -0
- package/codeAgent/tools/McpOAuth.ts +233 -0
- package/codeAgent/tools/McpOAuthLoopback.ts +64 -0
- package/codeAgent/tools/McpServerConfig.ts +129 -0
- package/codeAgent/tools/McpSignIn.ts +158 -0
- package/codeAgent/tools/McpTokenStore.ts +81 -0
- package/codeAgent/tools/McpToolPack.ts +199 -0
- package/codeAgent/tools/SessionToolPack.ts +91 -0
- package/codeAgent/tools/WebToolPack.ts +147 -0
- package/codeAgent/tools/akanCodePlugins.test.ts +677 -0
- package/codeAgent/tools/askToolPack.test.ts +116 -0
- package/codeAgent/tools/mcpOAuth.test.ts +612 -0
- package/codeAgent/tools/mcpServerConfig.test.ts +212 -0
- package/codegenLock.test.ts +4 -13
- package/codegenLock.ts +3 -17
- package/commandDecorators/argMeta.ts +1 -5
- package/commandDecorators/camelToKebabCase.ts +1 -0
- package/commandDecorators/command.ts +46 -102
- package/commandDecorators/commandBuilder.ts +30 -61
- package/commandDecorators/commandDecorators.test.ts +2 -10
- package/commandDecorators/dependencyBuilder.ts +10 -1
- package/commandDecorators/helpFormatter.ts +68 -145
- package/commandDecorators/index.ts +0 -2
- package/commandDecorators/targetMeta.ts +9 -7
- package/createTunnel.ts +7 -14
- package/csrE2e/csrE2eHarness.fixture.ts +505 -0
- package/csrE2e/csrFastRefresh.e2e.test.ts +233 -0
- package/csrE2e/csrPageStack.e2e.test.ts +252 -0
- package/csrE2e/csrPatchFirst.e2e.test.ts +87 -0
- package/csrE2e/csrRootLayout.e2e.test.ts +134 -0
- package/csrE2e/devHmrLatency.e2e.test.ts +391 -0
- package/csrE2e/registryVendor.e2e.test.ts +221 -0
- package/csrE2e/ssrRegistry.e2e.test.ts +644 -0
- package/dependencyScanner.test.ts +4 -16
- package/dependencyScanner.ts +31 -80
- package/devkitUtils.test.ts +48 -20
- package/executors.linkFallback.test.ts +103 -0
- package/executors.test.ts +176 -106
- package/executors.ts +212 -498
- package/fileEditor.ts +9 -37
- package/{getDirname.test.ts → fileSys.test.ts} +5 -3
- package/fileSys.ts +22 -9
- package/formSetterScanner.ts +3 -16
- package/frontendBuild/allRoutesBuilder.ts +7 -21
- package/frontendBuild/autoImportSync.test.ts +27 -38
- package/frontendBuild/autoImportSync.ts +31 -65
- package/frontendBuild/buildRouteClient.test.ts +123 -37
- package/frontendBuild/bundleDefine.test.ts +12 -0
- package/frontendBuild/bundleDefine.ts +16 -0
- package/frontendBuild/clientBuildTypes.ts +5 -36
- package/frontendBuild/clientEntriesBundler.ts +9 -40
- package/frontendBuild/clientEntryDiscovery.test.ts +112 -0
- package/frontendBuild/clientEntryDiscovery.ts +122 -110
- package/frontendBuild/csrArtifactBuilder.ts +9 -31
- package/frontendBuild/csrDevArtifactWriter.test.ts +151 -0
- package/frontendBuild/csrDevArtifactWriter.ts +307 -0
- package/frontendBuild/csrDevBundler.test.ts +66 -0
- package/frontendBuild/csrDevBundler.ts +95 -0
- package/frontendBuild/csrDevFactoryCheck.test.ts +33 -0
- package/frontendBuild/csrDevFactoryCheck.ts +39 -0
- package/frontendBuild/csrDevModuleCompiler.test.ts +293 -0
- package/frontendBuild/csrDevModuleCompiler.ts +422 -0
- package/frontendBuild/csrDevPatcher.test.ts +572 -0
- package/frontendBuild/csrDevPatcher.ts +548 -0
- package/frontendBuild/csrDevPaths.ts +145 -0
- package/frontendBuild/csrDevResolver.test.ts +239 -0
- package/frontendBuild/csrDevResolver.ts +363 -0
- package/frontendBuild/csrDevTypes.ts +61 -0
- package/frontendBuild/csrEntryFiles.ts +24 -0
- package/frontendBuild/cssCandidateCache.test.ts +0 -6
- package/frontendBuild/cssCandidateCache.ts +6 -41
- package/frontendBuild/cssCompiler.ts +31 -82
- package/frontendBuild/cssImportResolver.ts +6 -19
- package/frontendBuild/devChangePlanner.ts +7 -10
- package/frontendBuild/devGeneratedIndexSync.test.ts +12 -27
- package/frontendBuild/devGeneratedIndexSync.ts +1 -4
- package/frontendBuild/devRegistryBundler.ts +192 -0
- package/frontendBuild/fontOptimizer.test.ts +6 -14
- package/frontendBuild/fontOptimizer.ts +15 -72
- package/frontendBuild/fontPruner.test.ts +5 -10
- package/frontendBuild/fontPruner.ts +5 -20
- package/frontendBuild/frontendBuild.test.ts +57 -70
- package/frontendBuild/hmrChangeClassifier.ts +2 -2
- package/frontendBuild/hmrWatcher.test.ts +59 -32
- package/frontendBuild/hmrWatcher.ts +31 -74
- package/frontendBuild/index.ts +4 -0
- package/frontendBuild/pagesBundleBuilder.ts +28 -30
- package/frontendBuild/pagesEntrySourceGenerator.ts +56 -6
- package/frontendBuild/precompressArtifacts.ts +5 -18
- package/frontendBuild/registryMetadataFingerprint.test.ts +63 -0
- package/frontendBuild/registryMetadataFingerprint.ts +28 -0
- package/frontendBuild/routeClientBuilder.ts +100 -62
- package/frontendBuild/routesManifestArtifactSerializer.ts +5 -36
- package/frontendBuild/serverGraphFile.test.ts +77 -0
- package/frontendBuild/serverGraphFile.ts +93 -0
- package/frontendBuild/sourceMtimeIndex.test.ts +41 -97
- package/frontendBuild/sourceMtimeIndex.ts +19 -130
- package/frontendBuild/ssrBaseArtifactBuilder.ts +19 -13
- package/frontendBuild/ssrDevBundler.ts +85 -0
- package/frontendBuild/styleContract.ts +1 -8
- package/frontendBuild/themeValidator.ts +4 -26
- package/frontendBuild/watchRootResolver.test.ts +23 -42
- package/frontendBuild/watchRootResolver.ts +5 -27
- package/getRelatedCnsts.ts +12 -92
- package/incrementalBuilder/buildBatch.proc.ts +147 -63
- package/incrementalBuilder/buildBatchProtocol.ts +13 -38
- package/incrementalBuilder/buildBatchRunner.test.ts +65 -0
- package/incrementalBuilder/buildBatchRunner.ts +26 -38
- package/incrementalBuilder/builderChannel.test.ts +24 -49
- package/incrementalBuilder/builderChannel.ts +6 -37
- package/incrementalBuilder/builderRequestRouter.test.ts +0 -8
- package/incrementalBuilder/builderRequestRouter.ts +5 -24
- package/incrementalBuilder/builderWorkQueue.test.ts +94 -0
- package/incrementalBuilder/builderWorkQueue.ts +126 -0
- package/incrementalBuilder/devWatchBatch.test.ts +21 -24
- package/incrementalBuilder/incrementalBuilder.host.test.ts +53 -56
- package/incrementalBuilder/incrementalBuilder.host.ts +68 -81
- package/incrementalBuilder/incrementalBuilder.proc.test.ts +270 -0
- package/incrementalBuilder/incrementalBuilder.proc.ts +596 -208
- package/index.ts +5 -19
- package/integration/devResourceProbe.ts +50 -56
- package/integration/devStability.integration.test.ts +105 -199
- package/integration/devStabilityHarness.test.ts +13 -28
- package/integration/devStabilityHarness.ts +65 -330
- package/integration/ssrMemoryProbe.ts +41 -167
- package/libSource.test.ts +4 -15
- package/libSource.ts +19 -31
- package/lint/__fixtures__/no-document-cookie/bad.tsx +4 -0
- package/lint/__fixtures__/no-document-cookie/good.tsx +7 -0
- package/lint/__fixtures__/no-web-only-api-outside-webkit/bad.tsx +7 -0
- package/lint/__fixtures__/no-web-only-api-outside-webkit/good.tsx +11 -0
- package/lint/__fixtures__/no-web-storage/bad.tsx +6 -0
- package/lint/__fixtures__/no-web-storage/good.tsx +7 -0
- package/lint/gritRules.test.ts +8 -24
- package/lint/no-deep-internal-import.grit +2 -2
- package/lint/no-document-cookie.grit +29 -0
- package/lint/no-redeclare-predefined-endpoint.grit +2 -1
- package/lint/no-web-only-api-outside-webkit.grit +44 -0
- package/lint/no-web-storage.grit +30 -0
- package/linter.test.ts +1 -2
- package/linter.ts +26 -116
- package/mobile/desktopBin.test.ts +182 -0
- package/mobile/desktopBin.ts +144 -0
- package/mobile/desktopServerStage.test.ts +252 -0
- package/mobile/desktopServerStage.ts +178 -0
- package/mobile/index.ts +9 -0
- package/mobile/mobileTarget.test.ts +57 -8
- package/mobile/mobileTarget.ts +45 -26
- package/mobile/nativeApi.test.ts +30 -0
- package/mobile/nativeApi.ts +40 -0
- package/mobile/nativeApp.test.ts +184 -0
- package/mobile/nativeApp.ts +454 -0
- package/mobile/nativeAppLine.test.ts +69 -0
- package/mobile/nativeAppLine.ts +71 -0
- package/mobile/nativeConfig.test.ts +438 -0
- package/mobile/nativeConfig.ts +294 -0
- package/mobile/nativePluginFolders.test.ts +81 -0
- package/mobile/nativePluginFolders.ts +60 -0
- package/mobile/nativeWebDir.test.ts +80 -0
- package/mobile/nativeWebDir.ts +66 -0
- package/mobile/placeholderAppId.test.ts +13 -0
- package/mobile/placeholderAppId.ts +16 -0
- package/mobile/usageDescriptions.test.ts +27 -0
- package/mobile/usageDescriptions.ts +24 -0
- package/package.json +11 -11
- package/packageExports.test.ts +3 -18
- package/packageExportsMap.ts +3 -19
- package/piDependencyPins.test.ts +46 -0
- package/platformTest/BunTestReport.test.ts +60 -0
- package/platformTest/BunTestReport.ts +65 -0
- package/platformTest/IdleWatchedProcess.test.ts +56 -0
- package/platformTest/IdleWatchedProcess.ts +73 -0
- package/platformTest/LinuxDockerTarget.ts +105 -0
- package/platformTest/PlatformTestRun.test.ts +64 -0
- package/platformTest/PlatformTestRun.ts +141 -0
- package/platformTest/PlatformTestTarget.ts +142 -0
- package/platformTest/TestSnapshot.test.ts +72 -0
- package/platformTest/TestSnapshot.ts +103 -0
- package/platformTest/WindowsSshTarget.ts +151 -0
- package/prompter.ts +3 -13
- package/qualityScanner.test.ts +9 -26
- package/qualityScanner.ts +200 -286
- package/recipeScanner.test.ts +7 -12
- package/recipeScanner.ts +9 -38
- package/repoIdentity.ts +2 -9
- package/routeSourceValidator.test.ts +0 -1
- package/routeSourceValidator.ts +7 -42
- package/scanInfo.ts +91 -225
- package/selectModel.ts +1 -2
- package/semver.ts +3 -5
- package/slicePlanner.test.ts +6 -24
- package/slicePlanner.ts +11 -32
- package/spinner.test.ts +3 -8
- package/spinner.ts +4 -16
- package/sshTunnel.ts +1 -6
- package/ssrScanner.test.ts +1 -6
- package/ssrScanner.ts +103 -113
- package/stripAnsi.ts +3 -0
- package/subspace.test.ts +9 -25
- package/subspace.ts +48 -115
- package/subspaceConfig.ts +3 -11
- package/testHelpers.ts +194 -0
- package/transforms/asyncDefaultExportDetector.ts +3 -11
- package/transforms/barrelAnalyzer.ts +43 -135
- package/transforms/barrelImportsPlugin.ts +38 -175
- package/transforms/externalizeFrameworkPlugin.test.ts +39 -118
- package/transforms/externalizeFrameworkPlugin.ts +13 -87
- package/transforms/moduleSyntax.ts +32 -0
- package/transforms/rscUseClientTransform.ts +42 -20
- package/transforms/serverEnvPlugin.test.ts +103 -0
- package/transforms/serverEnvPlugin.ts +41 -0
- package/transforms/transforms.test.ts +166 -44
- package/transforms/tsconfigPackageResolver.test.ts +6 -24
- package/transforms/useClientBundlePlugin.ts +6 -23
- package/typeChecker.ts +7 -63
- package/types.ts +2 -0
- package/uploadRelease.ts +10 -25
- package/workflow/artifacts.ts +18 -0
- package/workflow/executor.test.ts +1 -12
- package/workflow/executor.ts +101 -110
- package/workflow/index.ts +0 -1
- package/workflow/moduleIndex.test.ts +4 -15
- package/workflow/moduleIndex.ts +9 -49
- package/workflow/plan.ts +18 -25
- package/workflow/render.ts +80 -96
- package/workflow/rolloutGate.test.ts +1 -1
- package/workflow/source.ts +52 -69
- package/workflow/sourceAst.ts +50 -0
- package/workspaceLayout.test.ts +18 -3
- package/workspaceLayout.ts +22 -23
- package/abstractCompactor.ts +0 -112
- package/aiEditor.test.ts +0 -60
- package/aiEditor.ts +0 -387
- package/applicationReleasePackager.ts +0 -209
- package/capacitorApp.test.ts +0 -372
- package/capacitorApp.ts +0 -1648
- package/commandDecorators/commandMeta.ts +0 -7
- package/commandDecorators/types.ts +0 -10
- package/getDirname.ts +0 -4
- package/guideline.ts +0 -19
- package/local/optimize-resource/ipcprobe/child.ts +0 -1
- package/local/optimize-resource/ipcprobe/parent.ts +0 -11
- package/workflow/primitive.ts +0 -59
package/dependencyScanner.ts
CHANGED
|
@@ -7,11 +7,12 @@ import { FileSys } from "./fileSys";
|
|
|
7
7
|
import type { PackageJson, TsConfigJson } from "./types";
|
|
8
8
|
|
|
9
9
|
const testFileRegex = /\.(?:test|spec)\.[cm]?[tj]sx?$/;
|
|
10
|
-
// `__fixtures__` holds deliberately invalid
|
|
11
|
-
// biome's `!` override already skip them, and their imports are violations rather than real dependencies.
|
|
10
|
+
// `__fixtures__` holds deliberately invalid lint-rule samples, whose imports are violations, not dependencies.
|
|
12
11
|
const skipDirs = ["node_modules", "dist", "build", ".git", ".next", "public", "ios", "android", "__fixtures__"];
|
|
13
|
-
const isSkippedPath = (filePath: string) =>
|
|
14
|
-
|
|
12
|
+
const isSkippedPath = (filePath: string) => {
|
|
13
|
+
const posixPath = filePath.split(path.sep).join("/");
|
|
14
|
+
return skipDirs.some((dir) => posixPath.includes(`/${dir}/`) || posixPath.startsWith(`${dir}/`));
|
|
15
|
+
};
|
|
15
16
|
const builtinModuleSet = new Set([...builtinModules, ...builtinModules.map((mod) => `node:${mod}`)]);
|
|
16
17
|
const stripShebang = (source: string) => source.replace(/^#!.*(?:\r?\n|$)/, "");
|
|
17
18
|
|
|
@@ -73,11 +74,8 @@ export class TypeScriptDependencyScanner {
|
|
|
73
74
|
): Promise<{ npmDeps: string[]; npmDevDeps: string[]; missingDeps: string[] }> {
|
|
74
75
|
const runtimeDeps = new Set<string>();
|
|
75
76
|
const devDeps = new Set<string>();
|
|
76
|
-
const sourceFiles = await this.#
|
|
77
|
-
|
|
78
|
-
excludeTestFiles: true,
|
|
79
|
-
});
|
|
80
|
-
const cssFiles = await this.#findCssFiles(this.#directory);
|
|
77
|
+
const sourceFiles = await this.#findFiles("**/*.{ts,tsx}", { excludeBuildFiles: true, excludeTestFiles: true });
|
|
78
|
+
const cssFiles = await this.#findFiles("**/*.css");
|
|
81
79
|
|
|
82
80
|
for (const filePath of sourceFiles) {
|
|
83
81
|
const fileContent = await FileSys.readText(filePath);
|
|
@@ -133,25 +131,19 @@ export class TypeScriptDependencyScanner {
|
|
|
133
131
|
depSets: DepSets,
|
|
134
132
|
fileDependencies: Map<string, string[]>,
|
|
135
133
|
): DepSets {
|
|
136
|
-
const importedDepSets = new
|
|
137
|
-
for (
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (depSets[j]?.has(importName)) {
|
|
148
|
-
importedDepSets[j]?.add(importName);
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
134
|
+
const importedDepSets = depSets.map(() => new Set<string>());
|
|
135
|
+
for (const imps of fileDependencies.values())
|
|
136
|
+
for (const imp of imps) {
|
|
137
|
+
if (imp.startsWith(".")) continue;
|
|
138
|
+
const parts = imp.split("/");
|
|
139
|
+
for (let length = 1; length <= parts.length; length++) {
|
|
140
|
+
const importName = parts.slice(0, length).join("/");
|
|
141
|
+
const setIndex = depSets.findIndex((depSet) => depSet.has(importName));
|
|
142
|
+
if (setIndex === -1) continue;
|
|
143
|
+
importedDepSets[setIndex]?.add(importName);
|
|
144
|
+
break;
|
|
152
145
|
}
|
|
153
|
-
}
|
|
154
|
-
});
|
|
146
|
+
}
|
|
155
147
|
return importedDepSets as DepSets;
|
|
156
148
|
}
|
|
157
149
|
|
|
@@ -161,48 +153,24 @@ export class TypeScriptDependencyScanner {
|
|
|
161
153
|
this.#fileTypeDependencies.clear();
|
|
162
154
|
this.#visitedFiles.clear();
|
|
163
155
|
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
for (const file of files) await this.#analyzeFile(file, this.#directory);
|
|
167
|
-
|
|
156
|
+
for (const file of await this.#findFiles("**/*.{ts,tsx}")) await this.#analyzeFile(file, this.#directory);
|
|
168
157
|
return this.#fileDependencies;
|
|
169
158
|
}
|
|
170
159
|
|
|
171
|
-
async #
|
|
172
|
-
|
|
160
|
+
async #findFiles(
|
|
161
|
+
pattern: string,
|
|
173
162
|
{
|
|
174
163
|
excludeBuildFiles = false,
|
|
175
164
|
excludeTestFiles = false,
|
|
176
165
|
}: { excludeBuildFiles?: boolean; excludeTestFiles?: boolean } = {},
|
|
177
166
|
): Promise<string[]> {
|
|
178
167
|
const files: string[] = [];
|
|
179
|
-
|
|
180
|
-
const glob = new Bun.Glob("**/*.{ts,tsx}");
|
|
181
|
-
for await (const filePath of glob.scan({ cwd: directory, onlyFiles: true })) {
|
|
168
|
+
for await (const filePath of new Bun.Glob(pattern).scan({ cwd: this.#directory, onlyFiles: true })) {
|
|
182
169
|
if (isSkippedPath(filePath)) continue;
|
|
183
170
|
if (excludeBuildFiles && filePath === "build.ts") continue;
|
|
184
171
|
if (excludeTestFiles && testFileRegex.test(filePath)) continue;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const relativePath = path.relative(this.#workspaceRoot, fullPath);
|
|
188
|
-
if (this.#ig.ignores(relativePath)) continue;
|
|
189
|
-
|
|
190
|
-
files.push(fullPath);
|
|
191
|
-
}
|
|
192
|
-
return files;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async #findCssFiles(directory: string): Promise<string[]> {
|
|
196
|
-
const files: string[] = [];
|
|
197
|
-
const glob = new Bun.Glob("**/*.css");
|
|
198
|
-
for await (const filePath of glob.scan({ cwd: directory, onlyFiles: true })) {
|
|
199
|
-
if (isSkippedPath(filePath)) continue;
|
|
200
|
-
|
|
201
|
-
const fullPath = path.join(directory, filePath);
|
|
202
|
-
const relativePath = path.relative(this.#workspaceRoot, fullPath);
|
|
203
|
-
if (this.#ig.ignores(relativePath)) continue;
|
|
204
|
-
|
|
205
|
-
files.push(fullPath);
|
|
172
|
+
const fullPath = path.join(this.#directory, filePath);
|
|
173
|
+
if (!this.#ig.ignores(path.relative(this.#workspaceRoot, fullPath))) files.push(fullPath);
|
|
206
174
|
}
|
|
207
175
|
return files;
|
|
208
176
|
}
|
|
@@ -215,12 +183,8 @@ export class TypeScriptDependencyScanner {
|
|
|
215
183
|
try {
|
|
216
184
|
const fileContent = await FileSys.readText(filePath);
|
|
217
185
|
const { imports, typeImports } = this.#extractImports(fileContent, filePath);
|
|
218
|
-
|
|
219
|
-
// Convert imports to absolute or relative paths
|
|
220
186
|
const resolvedImports = await this.#resolveImports(imports, filePath, baseDir);
|
|
221
187
|
const resolvedTypeImports = await this.#resolveImports(typeImports, filePath, baseDir);
|
|
222
|
-
|
|
223
|
-
// Store the dependencies
|
|
224
188
|
const relativePath = path.relative(baseDir, filePath);
|
|
225
189
|
this.#fileDependencies.set(relativePath, [...new Set([...resolvedImports, ...resolvedTypeImports])]);
|
|
226
190
|
this.#fileRuntimeDependencies.set(relativePath, resolvedImports);
|
|
@@ -233,12 +197,8 @@ export class TypeScriptDependencyScanner {
|
|
|
233
197
|
async #resolveImports(imports: string[], filePath: string, baseDir: string): Promise<string[]> {
|
|
234
198
|
return await Promise.all(
|
|
235
199
|
imports.map(async (importPath) => {
|
|
236
|
-
if (importPath.startsWith("."))
|
|
237
|
-
|
|
238
|
-
const resolvedPath = `./${path.join(path.relative(baseDir, filePath), importPath)}`;
|
|
239
|
-
return await this.#ensureExtension(resolvedPath);
|
|
240
|
-
}
|
|
241
|
-
return importPath; // Keep package imports as is
|
|
200
|
+
if (!importPath.startsWith(".")) return importPath;
|
|
201
|
+
return await this.#ensureExtension(`./${path.join(path.relative(baseDir, filePath), importPath)}`);
|
|
242
202
|
}),
|
|
243
203
|
);
|
|
244
204
|
}
|
|
@@ -324,19 +284,10 @@ export class TypeScriptDependencyScanner {
|
|
|
324
284
|
const projectImports = imports.filter((i) => !i.startsWith("react") && !i.startsWith("@"));
|
|
325
285
|
const externalImports = imports.filter((i) => i.startsWith("react") || i.startsWith("@"));
|
|
326
286
|
|
|
327
|
-
if (projectImports.length > 0)
|
|
328
|
-
graph +=
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (externalImports.length > 0) {
|
|
335
|
-
graph += " External dependencies:\n";
|
|
336
|
-
externalImports.forEach((imp) => {
|
|
337
|
-
graph += ` → ${imp}\n`;
|
|
338
|
-
});
|
|
339
|
-
}
|
|
287
|
+
if (projectImports.length > 0)
|
|
288
|
+
graph += ` Project dependencies:\n${projectImports.map((imp) => ` → ${imp}\n`).join("")}`;
|
|
289
|
+
if (externalImports.length > 0)
|
|
290
|
+
graph += ` External dependencies:\n${externalImports.map((imp) => ` → ${imp}\n`).join("")}`;
|
|
340
291
|
|
|
341
292
|
graph += "\n";
|
|
342
293
|
}
|
package/devkitUtils.test.ts
CHANGED
|
@@ -1,29 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import os from "node:os";
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { realpath } from "node:fs/promises";
|
|
4
3
|
import path from "node:path";
|
|
5
4
|
import { ApplicationBuildReporter } from "./applicationBuildReporter";
|
|
6
5
|
import { resolveSignalTestPreloadPath } from "./applicationTestPreload";
|
|
7
6
|
import { TypeScriptDependencyScanner } from "./dependencyScanner";
|
|
8
7
|
import { AppExecutor, WorkspaceExecutor } from "./executors";
|
|
8
|
+
import { tempDirs, writeText as write } from "./testHelpers";
|
|
9
9
|
import type { PackageJson, TsConfigJson } from "./types";
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const makeTempRoot = async () => {
|
|
14
|
-
const root = await mkdtemp(path.join(os.tmpdir(), "akan-devkit-utils-"));
|
|
15
|
-
tempRoots.push(root);
|
|
16
|
-
return root;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const write = async (filePath: string, content: string) => {
|
|
20
|
-
await mkdir(path.dirname(filePath), { recursive: true });
|
|
21
|
-
await writeFile(filePath, content);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
afterEach(async () => {
|
|
25
|
-
await Promise.all(tempRoots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
|
26
|
-
});
|
|
11
|
+
const makeTempRoot = tempDirs("akan-devkit-utils-");
|
|
27
12
|
|
|
28
13
|
describe("resolveSignalTestPreloadPath", () => {
|
|
29
14
|
test("resolves the preload file from an installed akanjs package", async () => {
|
|
@@ -40,7 +25,7 @@ describe("resolveSignalTestPreloadPath", () => {
|
|
|
40
25
|
await write(path.join(root, "node_modules/akanjs/test/signalTest.preload.ts"), "export {};\n");
|
|
41
26
|
|
|
42
27
|
await expect(resolveSignalTestPreloadPath({ cwdPath: libDir })).resolves.toContain(
|
|
43
|
-
"node_modules/akanjs/test/signalTest.preload.ts",
|
|
28
|
+
path.join("node_modules/akanjs/test/signalTest.preload.ts"),
|
|
44
29
|
);
|
|
45
30
|
});
|
|
46
31
|
});
|
|
@@ -213,4 +198,47 @@ describe("ApplicationBuildReporter", () => {
|
|
|
213
198
|
["failed", " first", " second", " third"].join("\n"),
|
|
214
199
|
);
|
|
215
200
|
});
|
|
201
|
+
|
|
202
|
+
test("says where the bundler placed each reason, relative to the workspace root", async () => {
|
|
203
|
+
const root = await makeTempRoot();
|
|
204
|
+
const refuseWith = (message: string) => ({
|
|
205
|
+
name: "refuse",
|
|
206
|
+
setup: (build: Bun.PluginBuilder) => {
|
|
207
|
+
build.onLoad({ filter: /plugged\.ts$/ }, () => {
|
|
208
|
+
throw new Error(message);
|
|
209
|
+
});
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
const failureOf = (entry: string, plugins: Bun.BunPlugin[] = []) =>
|
|
213
|
+
Bun.build({ entrypoints: [path.join(root, entry)], plugins }).then(
|
|
214
|
+
() => null,
|
|
215
|
+
(error: unknown) => error,
|
|
216
|
+
);
|
|
217
|
+
await write(
|
|
218
|
+
path.join(root, "src/entry.ts"),
|
|
219
|
+
'export const a = 1;\nimport { gone } from "./not-there";\nexport const b = gone;\n',
|
|
220
|
+
);
|
|
221
|
+
await write(path.join(root, "src/plugged.ts"), "export const b = 1;\n");
|
|
222
|
+
|
|
223
|
+
const unresolved = await failureOf("src/entry.ts");
|
|
224
|
+
expect(ApplicationBuildReporter.formatError(unresolved, root)).toBe(
|
|
225
|
+
["Bundle failed", ' Could not resolve: "./not-there" (src/entry.ts:2:22)'].join("\n"),
|
|
226
|
+
);
|
|
227
|
+
const outside = path.join(await realpath(root), "src/entry.ts");
|
|
228
|
+
expect(ApplicationBuildReporter.formatError(unresolved, path.join(root, "elsewhere"))).toBe(
|
|
229
|
+
["Bundle failed", ` Could not resolve: "./not-there" (${outside}:2:22)`].join("\n"),
|
|
230
|
+
);
|
|
231
|
+
expect(
|
|
232
|
+
ApplicationBuildReporter.formatError(
|
|
233
|
+
await failureOf("src/plugged.ts", [refuseWith("refused\nsecond line")]),
|
|
234
|
+
root,
|
|
235
|
+
),
|
|
236
|
+
).toBe(["Bundle failed", " refused (src/plugged.ts)", " second line"].join("\n"));
|
|
237
|
+
expect(
|
|
238
|
+
ApplicationBuildReporter.formatError(
|
|
239
|
+
await failureOf("src/plugged.ts", [refuseWith("src/plugged.ts is refused")]),
|
|
240
|
+
root,
|
|
241
|
+
),
|
|
242
|
+
).toBe(["Bundle failed", " src/plugged.ts is refused"].join("\n"));
|
|
243
|
+
});
|
|
216
244
|
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
import * as fsPromises from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
//? The Windows VM runs as an administrator, so a refused link only ever happens here — injected, on every OS.
|
|
7
|
+
const realSymlink = fsPromises.symlink;
|
|
8
|
+
let refuseLinks = false;
|
|
9
|
+
mock.module("node:fs/promises", () => ({
|
|
10
|
+
...fsPromises,
|
|
11
|
+
symlink: async (...args: Parameters<typeof fsPromises.symlink>) => {
|
|
12
|
+
if (refuseLinks)
|
|
13
|
+
throw Object.assign(new Error(`EPERM: operation not permitted, symlink '${String(args[0])}'`), {
|
|
14
|
+
code: "EPERM",
|
|
15
|
+
});
|
|
16
|
+
return await realSymlink(...args);
|
|
17
|
+
},
|
|
18
|
+
}));
|
|
19
|
+
const { AppExecutor, WorkspaceExecutor } = await import("./executors");
|
|
20
|
+
|
|
21
|
+
const { lstat, mkdir, mkdtemp, readFile, rm, writeFile } = fsPromises;
|
|
22
|
+
const originalEnv = { ...process.env };
|
|
23
|
+
const originalPlatform = process.platform;
|
|
24
|
+
const tempRoots: string[] = [];
|
|
25
|
+
const PAGE_SOURCE = "export default function Page() {\n return null;\n}\n";
|
|
26
|
+
|
|
27
|
+
const setPlatform = (platform: NodeJS.Platform) =>
|
|
28
|
+
Object.defineProperty(process, "platform", { value: platform, configurable: true, writable: true });
|
|
29
|
+
|
|
30
|
+
// `AppExecutor.from` memoises by name, so each test needs a name no other test has used.
|
|
31
|
+
const makeApp = async (appName: string) => {
|
|
32
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "akan-link-fallback-"));
|
|
33
|
+
tempRoots.push(root);
|
|
34
|
+
process.env.AKAN_PUBLIC_REPO_NAME = "repo";
|
|
35
|
+
process.env.AKAN_PUBLIC_SERVE_DOMAIN = "example.com";
|
|
36
|
+
process.env.AKAN_PUBLIC_ENV = "local";
|
|
37
|
+
process.env.PORT_OFFSET = "0";
|
|
38
|
+
await writeFile(path.join(root, "package.json"), JSON.stringify({ name: "fixture", version: "1.0.0" }));
|
|
39
|
+
await mkdir(path.join(root, "libs/shared/public"), { recursive: true });
|
|
40
|
+
await writeFile(path.join(root, "libs/shared/public/logo.png"), "logo");
|
|
41
|
+
await mkdir(path.join(root, "libs/shared/page/about"), { recursive: true });
|
|
42
|
+
await writeFile(path.join(root, "libs/shared/page/about/_index.tsx"), PAGE_SOURCE);
|
|
43
|
+
await mkdir(path.join(root, "apps", appName, "page"), { recursive: true });
|
|
44
|
+
await writeFile(path.join(root, "apps", appName, "akan.config.ts"), "export default { syncPageLibs: true };\n");
|
|
45
|
+
const workspace = new WorkspaceExecutor({ workspaceRoot: root, repoName: "repo" });
|
|
46
|
+
return { root, app: AppExecutor.from(workspace, appName), appRoot: path.join(root, "apps", appName) };
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
process.env = { ...originalEnv };
|
|
51
|
+
refuseLinks = true;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
afterEach(async () => {
|
|
55
|
+
refuseLinks = false;
|
|
56
|
+
setPlatform(originalPlatform);
|
|
57
|
+
process.env = { ...originalEnv };
|
|
58
|
+
await Promise.all(tempRoots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("lib links the OS refuses to create", () => {
|
|
62
|
+
test("Windows gets a copy of the lib assets, and a resync replaces it without touching the lib", async () => {
|
|
63
|
+
setPlatform("win32");
|
|
64
|
+
const { root, app, appRoot } = await makeApp("fallback-assets");
|
|
65
|
+
|
|
66
|
+
await app.syncAssets(["shared"]);
|
|
67
|
+
const copied = path.join(appRoot, "public/libs/shared");
|
|
68
|
+
expect((await lstat(copied)).isSymbolicLink()).toBe(false);
|
|
69
|
+
expect(await readFile(path.join(copied, "logo.png"), "utf8")).toBe("logo");
|
|
70
|
+
|
|
71
|
+
await writeFile(path.join(root, "libs/shared/public/logo.png"), "logo-v2");
|
|
72
|
+
await app.syncAssets(["shared"]);
|
|
73
|
+
expect(await readFile(path.join(copied, "logo.png"), "utf8")).toBe("logo-v2");
|
|
74
|
+
|
|
75
|
+
await app.syncAssets([]);
|
|
76
|
+
expect(await lstat(path.join(appRoot, "public/libs")).catch(() => null)).toBeNull();
|
|
77
|
+
expect(await readFile(path.join(root, "libs/shared/public/logo.png"), "utf8")).toBe("logo-v2");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("Windows gets a copy of the lib pages that still routes, and a resync picks up a new lib route", async () => {
|
|
81
|
+
setPlatform("win32");
|
|
82
|
+
const { root, app, appRoot } = await makeApp("fallback-pages");
|
|
83
|
+
|
|
84
|
+
expect(await app.syncPages(["shared"])).toBe(true);
|
|
85
|
+
expect((await lstat(path.join(appRoot, "page/(libs)/(shared)"))).isSymbolicLink()).toBe(false);
|
|
86
|
+
expect(await app.getPageKeys({ refresh: true })).toEqual(["./(libs)/(shared)/about/_index.tsx"]);
|
|
87
|
+
|
|
88
|
+
await mkdir(path.join(root, "libs/shared/page/terms"), { recursive: true });
|
|
89
|
+
await writeFile(path.join(root, "libs/shared/page/terms/_index.tsx"), PAGE_SOURCE);
|
|
90
|
+
expect(await app.syncPages(["shared"])).toBe(true);
|
|
91
|
+
expect(await app.getPageKeys({ refresh: true })).toEqual([
|
|
92
|
+
"./(libs)/(shared)/about/_index.tsx",
|
|
93
|
+
"./(libs)/(shared)/terms/_index.tsx",
|
|
94
|
+
]);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("elsewhere the refusal surfaces rather than being papered over with a copy", async () => {
|
|
98
|
+
setPlatform(originalPlatform === "win32" ? "linux" : originalPlatform);
|
|
99
|
+
const { app } = await makeApp("fallback-posix");
|
|
100
|
+
|
|
101
|
+
await expect(app.syncAssets(["shared"])).rejects.toThrow("EPERM");
|
|
102
|
+
});
|
|
103
|
+
});
|