akanjs 2.0.0-rc.0 → 2.0.0-rc.2

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/cli/index.js CHANGED
@@ -9346,7 +9346,7 @@ import {
9346
9346
  fork,
9347
9347
  spawn
9348
9348
  } from "child_process";
9349
- import { mkdir as mkdir2, stat as stat2 } from "fs/promises";
9349
+ import { mkdir as mkdir2, readdir as readDirEntries, stat as stat2 } from "fs/promises";
9350
9350
  import path7 from "path";
9351
9351
  import chalk4 from "chalk";
9352
9352
 
@@ -11074,7 +11074,7 @@ class Executor {
11074
11074
  const fileContent = await this.#applyTemplateFile({ templatePath: prefixTemplatePath, targetPath: path7.join(basePath2, filename), scanInfo, overwrite }, dict, options);
11075
11075
  return fileContent ? [fileContent] : [];
11076
11076
  } else {
11077
- const subdirs = await this.readdir(templatePath);
11077
+ const subdirs = await readDirEntries(templatePath);
11078
11078
  const fileContents = (await Promise.all(subdirs.map(async (subdir) => {
11079
11079
  const subpath = path7.join(templatePath, subdir);
11080
11080
  if ((await stat2(subpath)).isFile()) {
@@ -11198,6 +11198,9 @@ class WorkspaceExecutor extends Executor {
11198
11198
  if (type === "lib" || type === "pkg")
11199
11199
  rootTsConfig.compilerOptions.paths[`@${name}`] = [`${type}s/${name}/index.ts`];
11200
11200
  rootTsConfig.compilerOptions.paths[`@${name}/*`] = [`${type}s/${name}/*`];
11201
+ rootTsConfig.compilerOptions.paths[`@${type}s/${name}/*`] = [`${type}s/${name}/*`];
11202
+ if (type === "lib" || type === "pkg")
11203
+ rootTsConfig.compilerOptions.paths[`@${type}s/${name}`] = [`${type}s/${name}/index.ts`];
11201
11204
  if (rootTsConfig.references) {
11202
11205
  if (!rootTsConfig.references.some((ref) => ref.path === `./${type}s/${name}/tsconfig.json`))
11203
11206
  rootTsConfig.references.push({ path: `./${type}s/${name}/tsconfig.json` });
@@ -11207,7 +11210,7 @@ class WorkspaceExecutor extends Executor {
11207
11210
  }
11208
11211
  async unsetTsPaths(type, name) {
11209
11212
  const rootTsConfig = await this.readJson("tsconfig.json");
11210
- const filteredKeys = Object.keys(rootTsConfig.compilerOptions.paths ?? {}).filter((key) => !key.startsWith(`@${name}`));
11213
+ const filteredKeys = Object.keys(rootTsConfig.compilerOptions.paths ?? {}).filter((key) => !key.startsWith(`@${name}`) && !key.startsWith(`@${type}s/${name}`));
11211
11214
  rootTsConfig.compilerOptions.paths = Object.fromEntries(filteredKeys.map((key) => [key, rootTsConfig.compilerOptions.paths?.[key] ?? []]));
11212
11215
  if (rootTsConfig.references) {
11213
11216
  rootTsConfig.references = rootTsConfig.references.filter((ref) => !ref.path.startsWith(`./${type}s/${name}`));
@@ -12737,6 +12740,8 @@ class BarrelAnalyzer {
12737
12740
  const rel = path12.relative(pkg.pkgDir, absFile);
12738
12741
  if (!rel || rel.startsWith("..") || path12.isAbsolute(rel))
12739
12742
  return null;
12743
+ if (pkg.preserveFilePath)
12744
+ return `${pkg.pkgName}/${rel.split(path12.sep).join("/")}`;
12740
12745
  const noExt = stripKnownExt(rel);
12741
12746
  const tail = collapseIndex(noExt);
12742
12747
  if (tail === "")
@@ -12817,7 +12822,7 @@ var collapseIndex = (relPathNoExt) => {
12817
12822
 
12818
12823
  import path13 from "path";
12819
12824
  import ts3 from "typescript";
12820
- var createBarrelImportsPlugin = async (app, { skipPath = (p) => p.includes("node_modules"), pipeAfter } = {}) => {
12825
+ var createBarrelImportsPlugin = async (app, { skipPath = defaultSkipPath, pipeAfter } = {}) => {
12821
12826
  const akanConfig2 = await app.getConfig();
12822
12827
  const barrels = [...new Set(akanConfig2.barrelImports)].filter(Boolean);
12823
12828
  const analyzer = new BarrelAnalyzer({
@@ -12826,7 +12831,9 @@ var createBarrelImportsPlugin = async (app, { skipPath = (p) => p.includes("node
12826
12831
  return {
12827
12832
  name: "barrel-imports",
12828
12833
  setup(build) {
12829
- build.onLoad({ filter: /^(?!.*[\\/]node_modules[\\/]).*\.(tsx|ts|jsx|js)(\?v=\d+)?$/ }, async (args) => {
12834
+ build.onLoad({
12835
+ filter: /^(?:(?!.*[\\/]node_modules[\\/]).*|.*[\\/]node_modules[\\/]akanjs[\\/].*)\.(tsx|ts|jsx|js)(\?v=\d+)?$/
12836
+ }, async (args) => {
12830
12837
  const realPath = args.path.replace(/\?v=\d+$/, "");
12831
12838
  const loader = loaderFor(realPath);
12832
12839
  if (skipPath(realPath)) {
@@ -12936,6 +12943,9 @@ var createTsconfigPackageResolver = async (app) => {
12936
12943
  };
12937
12944
  };
12938
12945
  var CANDIDATE_EXTS2 = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
12946
+ var NODE_MODULES_RE = /[\\/]node_modules[\\/]/;
12947
+ var AKANJS_NODE_MODULE_RE = /[\\/]node_modules[\\/]akanjs[\\/]/;
12948
+ var defaultSkipPath = (absPath) => NODE_MODULES_RE.test(absPath) && !AKANJS_NODE_MODULE_RE.test(absPath);
12939
12949
  var resolveNodePackageExport = async (workspaceRoot, specifier) => {
12940
12950
  const packageName = getPackageName(specifier);
12941
12951
  if (!packageName)
@@ -12947,7 +12957,7 @@ var resolveNodePackageExport = async (workspaceRoot, specifier) => {
12947
12957
  const pkgDir = path13.dirname(pkgJsonPath);
12948
12958
  const pkgJson = JSON.parse(await Bun.file(pkgJsonPath).text());
12949
12959
  const subpath = specifier === packageName ? "." : `.${specifier.slice(packageName.length)}`;
12950
- const exported = resolveExportValue(pkgJson.exports?.[subpath]);
12960
+ const exported = resolvePackageExport(pkgJson.exports, subpath);
12951
12961
  const rel = exported ?? (subpath === "." ? pkgJson.module ?? pkgJson.main ?? "index.js" : null);
12952
12962
  if (!rel || !rel.startsWith("."))
12953
12963
  return null;
@@ -12955,7 +12965,7 @@ var resolveNodePackageExport = async (workspaceRoot, specifier) => {
12955
12965
  if (!entryFile)
12956
12966
  return null;
12957
12967
  const pkgEntryName = specifier;
12958
- return { pkgName: pkgEntryName, entryFile, pkgDir: path13.dirname(entryFile) };
12968
+ return { pkgName: pkgEntryName, entryFile, pkgDir: path13.dirname(entryFile), preserveFilePath: true };
12959
12969
  } catch {
12960
12970
  return null;
12961
12971
  }
@@ -12980,6 +12990,27 @@ var resolveExportValue = (value) => {
12980
12990
  }
12981
12991
  return null;
12982
12992
  };
12993
+ var resolvePackageExport = (exportsMap, subpath) => {
12994
+ if (!exportsMap)
12995
+ return null;
12996
+ const exact = resolveExportValue(exportsMap[subpath]);
12997
+ if (exact)
12998
+ return exact;
12999
+ for (const [key, value] of Object.entries(exportsMap)) {
13000
+ const starIdx = key.indexOf("*");
13001
+ if (starIdx === -1)
13002
+ continue;
13003
+ const prefix = key.slice(0, starIdx);
13004
+ const suffix = key.slice(starIdx + 1);
13005
+ if (!subpath.startsWith(prefix) || !subpath.endsWith(suffix))
13006
+ continue;
13007
+ const wildcard = subpath.slice(prefix.length, subpath.length - suffix.length);
13008
+ const resolved = resolveExportValue(value);
13009
+ if (resolved)
13010
+ return resolved.replace("*", wildcard);
13011
+ }
13012
+ return null;
13013
+ };
12983
13014
  var getPackageName = (specifier) => {
12984
13015
  const parts = specifier.split("/");
12985
13016
  if (!parts[0])
@@ -13173,7 +13204,10 @@ var loaderFor = (absPath) => {
13173
13204
 
13174
13205
  var USE_CLIENT_RE = /^\s*(?:\/\*[\s\S]*?\*\/\s*|\/\/[^\n]*\n\s*)*["']use client["']/;
13175
13206
  var SOURCE_EXTS2 = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
13207
+ var NODE_MODULES_RE2 = /[\\/]node_modules[\\/]/;
13208
+ var AKANJS_NODE_MODULE_RE2 = /[\\/]node_modules[\\/]akanjs[\\/]/;
13176
13209
  var NON_SOURCE_EXT_RE2 = /\.(css|scss|sass|less|json|svg|png|jpe?g|webp|gif|avif|ico|woff2?|ttf|otf|mp3|mp4|wav)$/i;
13210
+ var shouldSkipNodeModule = (absPath) => NODE_MODULES_RE2.test(absPath) && !AKANJS_NODE_MODULE_RE2.test(absPath);
13177
13211
 
13178
13212
  class GraphClientEntryDiscovery {
13179
13213
  #akanConfig;
@@ -13310,7 +13344,7 @@ class GraphClientEntryDiscovery {
13310
13344
  const cached = this.#reachableEntriesCache.get(absPath);
13311
13345
  if (cached)
13312
13346
  return new Set(cached);
13313
- if (visiting.has(absPath) || absPath.includes("node_modules"))
13347
+ if (visiting.has(absPath) || shouldSkipNodeModule(absPath))
13314
13348
  return new Set;
13315
13349
  visiting.add(absPath);
13316
13350
  const entries = new Set;
@@ -13333,7 +13367,7 @@ class GraphClientEntryDiscovery {
13333
13367
  const resolved = await this.#resolveSpecifier(spec, importerDir);
13334
13368
  if (!resolved)
13335
13369
  continue;
13336
- if (resolved.includes("node_modules"))
13370
+ if (shouldSkipNodeModule(resolved))
13337
13371
  continue;
13338
13372
  for (const entry of await this.#discoverFromFile(resolved, visiting))
13339
13373
  entries.add(entry);
@@ -1,10 +1,7 @@
1
- import { getEnv() } from "akanjs/base";
1
+ import { getEnv } from "akanjs/base";
2
2
 
3
3
  import type { ModulesOptions } from "../lib/option";
4
4
 
5
5
  export const env: ModulesOptions = {
6
6
  ...getEnv(),
7
- hostname: null,
8
- redis: {},
9
-
10
7
  };
@@ -1,5 +1,7 @@
1
1
  # organization configuration, no need to change
2
2
  AKAN_PUBLIC_REPO_NAME=<%= repoName %>
3
+
4
+ # serve domain, it changes the domain of the server.
3
5
  AKAN_PUBLIC_SERVE_DOMAIN="<%= serveDomain %>"
4
6
 
5
7
  # development branch, debug, develop, main, etc. mainly it changes databases.
@@ -7,14 +9,6 @@ AKAN_PUBLIC_ENV=local
7
9
 
8
10
  # local, cloud, edge it changes the connection point of the clients.
9
11
  AKAN_PUBLIC_OPERATION_MODE=local
10
- # hybrid app specific config, will be depreciated in the future
11
- APP_OPERATION_MODE=local
12
-
13
- # backend service mode, federation, batch, all
14
- SERVER_MODE=federation
15
-
16
- # analyze the bundle size
17
- ANALYZE=false
18
12
 
19
13
  # log level, debug, info, warn, error
20
14
  AKAN_PUBLIC_LOG_LEVEL=debug
@@ -0,0 +1,175 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/2.4.4/schema.json",
3
+ "vcs": {
4
+ "enabled": true,
5
+ "clientKind": "git",
6
+ "useIgnoreFile": false
7
+ },
8
+ "files": {
9
+ "includes": [
10
+ "**/*.ts",
11
+ "**/*.tsx",
12
+ "**/*.json",
13
+ "**/*.jsonc",
14
+ "!**/env.client.local.ts",
15
+ "!**/env.client.debug.ts",
16
+ "!**/env.client.develop.ts",
17
+ "!**/env.client.main.ts",
18
+ "!**/env.client.testing.ts",
19
+ "!**/env.server.local.ts",
20
+ "!**/env.server.develop.ts",
21
+ "!**/env.server.main.ts",
22
+ "!**/env.server.testing.ts",
23
+ "!**/env.server.type.ts",
24
+ "!apps/*/lib/cnst.ts",
25
+ "!apps/*/lib/dict.ts",
26
+ "!apps/*/lib/db.ts",
27
+ "!apps/*/lib/srv.ts",
28
+ "!apps/*/lib/st.ts",
29
+ "!apps/*/lib/sig.ts",
30
+ "!apps/*/lib/useClient.ts",
31
+ "!apps/*/lib/useServer.ts",
32
+ "!apps/*/client.ts",
33
+ "!apps/*/server.ts",
34
+ "!apps/*/lib/*/index.tsx",
35
+ "!libs/*/lib/cnst.ts",
36
+ "!libs/*/lib/dict.ts",
37
+ "!libs/*/lib/db.ts",
38
+ "!libs/*/lib/srv.ts",
39
+ "!libs/*/lib/st.ts",
40
+ "!libs/*/lib/sig.ts",
41
+ "!libs/*/lib/useClient.ts",
42
+ "!libs/*/lib/useServer.ts",
43
+ "!libs/*/client.ts",
44
+ "!libs/*/server.ts",
45
+ "!libs/*/index.ts",
46
+ "!libs/*/lib/*/index.tsx",
47
+ "!infra/master/createRecords.ts",
48
+ "!pkgs/contract/env.ts",
49
+ "!!**/node_modules",
50
+ "!!**/dist",
51
+ "!!**/.akan",
52
+ "!!**/dump",
53
+ "!!**/local"
54
+ ]
55
+ },
56
+ "formatter": {
57
+ "enabled": true,
58
+ "indentStyle": "space",
59
+ "indentWidth": 2,
60
+ "lineWidth": 120
61
+ },
62
+ "linter": {
63
+ "enabled": true,
64
+ "rules": {
65
+ "recommended": true,
66
+ "suspicious": {
67
+ "noConsole": {
68
+ "level": "error",
69
+ "options": {
70
+ "allow": ["assert", "error", "info", "warn"]
71
+ }
72
+ },
73
+ "noArrayIndexKey": "off",
74
+ "noShadowRestrictedNames": "off"
75
+ },
76
+ "correctness": {
77
+ "noUnusedFunctionParameters": "off",
78
+ "noUnusedImports": {
79
+ "level": "error",
80
+ "fix": "safe"
81
+ },
82
+ "useParseIntRadix": "off",
83
+ "useExhaustiveDependencies": "off"
84
+ },
85
+ "nursery": {
86
+ "useSortedClasses": {
87
+ "level": "error",
88
+ "fix": "safe",
89
+ "options": {
90
+ "attributes": ["classList"],
91
+ "functions": ["clsx", "cva"]
92
+ }
93
+ }
94
+ },
95
+ "a11y": "off",
96
+ "complexity": {
97
+ "noStaticOnlyClass": "off"
98
+ },
99
+ "style": {
100
+ "useTemplate": {
101
+ "level": "warn",
102
+ "fix": "safe"
103
+ }
104
+ }
105
+ },
106
+ "domains": {
107
+ "project": "recommended",
108
+ "react": "recommended",
109
+ "test": "recommended",
110
+ "types": "all"
111
+ }
112
+ },
113
+ "javascript": {
114
+ "parser": {
115
+ "unsafeParameterDecoratorsEnabled": true
116
+ },
117
+ "formatter": {
118
+ "quoteStyle": "double"
119
+ }
120
+ },
121
+ "assist": {
122
+ "enabled": true,
123
+ "actions": {
124
+ "source": {
125
+ "organizeImports": "on"
126
+ }
127
+ }
128
+ },
129
+ "overrides": [
130
+ {
131
+ "includes": ["**/page/**/*.ts", "**/page/**/*.tsx", "**/*.Unit.tsx", "**/*.View.tsx"],
132
+ "plugins": [
133
+ "./node_modules/akanjs/devkit/lint/no-import-client-functions.grit",
134
+ "./node_modules/akanjs/devkit/lint/no-use-client-in-server.grit"
135
+ ]
136
+ },
137
+ {
138
+ "includes": ["**/page/**/*.ts", "**/page/**/*.tsx"],
139
+ "plugins": ["./node_modules/akanjs/devkit/lint/non-scalar-props-restricted.grit"]
140
+ },
141
+ {
142
+ "includes": ["**/*.constant.ts", "**/*.document.ts", "**/*.service.ts", "**/*.store.ts"],
143
+ "plugins": ["./node_modules/akanjs/devkit/lint/no-js-private-class-method.grit"]
144
+ },
145
+ {
146
+ "includes": [
147
+ "**/page/**/*.ts",
148
+ "**/page/**/*.tsx",
149
+ "**/index.ts",
150
+ "**/index.tsx",
151
+ "**/cnst.ts",
152
+ "**/db.ts",
153
+ "**/dict.ts",
154
+ "**/option.ts",
155
+ "**/sig.ts",
156
+ "**/srv.ts",
157
+ "**/st.ts",
158
+ "**/*.constant.ts",
159
+ "**/*.dictionary.ts",
160
+ "**/*.document.ts",
161
+ "**/*.service.ts",
162
+ "**/*.signal.ts",
163
+ "**/*.signal.test.ts",
164
+ "**/*.service.test.ts",
165
+ "**/*.store.ts",
166
+ "**/*.Template.tsx",
167
+ "**/*.Unit.tsx",
168
+ "**/*.Util.tsx",
169
+ "**/*.View.tsx",
170
+ "**/*.Zone.tsx"
171
+ ],
172
+ "plugins": ["./node_modules/akanjs/devkit/lint/no-import-external-library.grit"]
173
+ }
174
+ ]
175
+ }
@@ -0,0 +1,4 @@
1
+
2
+ [serve.static]
3
+ plugins = ["bun-plugin-tailwind"]
4
+ env = "AKAN_PUBLIC_*"
@@ -10,6 +10,7 @@
10
10
  "daisyui": "^5.5.20"
11
11
  },
12
12
  "devDependencies": {
13
+ "@biomejs/biome": "2.4.4",
13
14
  "@types/react": "^19.2.14",
14
15
  "@types/react-dom": "^19.2.3",
15
16
  "crypto-browserify": "^3.12.1",
@@ -7,7 +7,7 @@ import {
7
7
  type SpawnOptions,
8
8
  spawn,
9
9
  } from "node:child_process";
10
- import { mkdir, stat } from "node:fs/promises";
10
+ import { mkdir, readdir as readDirEntries, stat } from "node:fs/promises";
11
11
  import path from "node:path";
12
12
  import {
13
13
  capitalize,
@@ -455,7 +455,7 @@ export class Executor {
455
455
  );
456
456
  return fileContent ? [fileContent] : ([] as FileContent[]);
457
457
  } else {
458
- const subdirs = await this.readdir(templatePath);
458
+ const subdirs = await readDirEntries(templatePath);
459
459
  const fileContents = (
460
460
  await Promise.all(
461
461
  subdirs.map(async (subdir) => {
@@ -615,6 +615,9 @@ export class WorkspaceExecutor extends Executor {
615
615
  if (type === "lib" || type === "pkg")
616
616
  rootTsConfig.compilerOptions.paths[`@${name}`] = [`${type}s/${name}/index.ts`];
617
617
  rootTsConfig.compilerOptions.paths[`@${name}/*`] = [`${type}s/${name}/*`];
618
+ rootTsConfig.compilerOptions.paths[`@${type}s/${name}/*`] = [`${type}s/${name}/*`];
619
+ if (type === "lib" || type === "pkg")
620
+ rootTsConfig.compilerOptions.paths[`@${type}s/${name}`] = [`${type}s/${name}/index.ts`];
618
621
  if (rootTsConfig.references) {
619
622
  if (!rootTsConfig.references.some((ref) => ref.path === `./${type}s/${name}/tsconfig.json`))
620
623
  rootTsConfig.references.push({ path: `./${type}s/${name}/tsconfig.json` });
@@ -625,7 +628,7 @@ export class WorkspaceExecutor extends Executor {
625
628
  async unsetTsPaths(type: "app" | "lib" | "pkg", name: string) {
626
629
  const rootTsConfig = (await this.readJson("tsconfig.json")) as TsConfigJson;
627
630
  const filteredKeys = Object.keys(rootTsConfig.compilerOptions.paths ?? {}).filter(
628
- (key) => !key.startsWith(`@${name}`),
631
+ (key) => !key.startsWith(`@${name}`) && !key.startsWith(`@${type}s/${name}`),
629
632
  );
630
633
  rootTsConfig.compilerOptions.paths = Object.fromEntries(
631
634
  filteredKeys.map((key) => [key, rootTsConfig.compilerOptions.paths?.[key] ?? []]),
@@ -6,10 +6,14 @@ import type { AkanConfig, ClientEntryDiscovery, ScannedImport } from "./clientBu
6
6
 
7
7
  const USE_CLIENT_RE = /^\s*(?:\/\*[\s\S]*?\*\/\s*|\/\/[^\n]*\n\s*)*["']use client["']/;
8
8
  const SOURCE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
9
+ const NODE_MODULES_RE = /[\\/]node_modules[\\/]/;
10
+ const AKANJS_NODE_MODULE_RE = /[\\/]node_modules[\\/]akanjs[\\/]/;
9
11
 
10
12
  const NON_SOURCE_EXT_RE = /\.(css|scss|sass|less|json|svg|png|jpe?g|webp|gif|avif|ico|woff2?|ttf|otf|mp3|mp4|wav)$/i;
11
13
  type PackageResolver = Awaited<ReturnType<typeof createTsconfigPackageResolver>>;
12
14
 
15
+ const shouldSkipNodeModule = (absPath: string) => NODE_MODULES_RE.test(absPath) && !AKANJS_NODE_MODULE_RE.test(absPath);
16
+
13
17
  /**
14
18
  * Graph-based `"use client"` discovery, seeded from an explicit file list.
15
19
  *
@@ -156,7 +160,7 @@ export class GraphClientEntryDiscovery implements ClientEntryDiscovery {
156
160
  const absPath = path.resolve(file);
157
161
  const cached = this.#reachableEntriesCache.get(absPath);
158
162
  if (cached) return new Set(cached);
159
- if (visiting.has(absPath) || absPath.includes("node_modules")) return new Set();
163
+ if (visiting.has(absPath) || shouldSkipNodeModule(absPath)) return new Set();
160
164
 
161
165
  visiting.add(absPath);
162
166
  const entries = new Set<string>();
@@ -177,7 +181,7 @@ export class GraphClientEntryDiscovery implements ClientEntryDiscovery {
177
181
  if (NON_SOURCE_EXT_RE.test(spec)) continue;
178
182
  const resolved = await this.#resolveSpecifier(spec, importerDir);
179
183
  if (!resolved) continue;
180
- if (resolved.includes("node_modules")) continue;
184
+ if (shouldSkipNodeModule(resolved)) continue;
181
185
  for (const entry of await this.#discoverFromFile(resolved, visiting)) entries.add(entry);
182
186
  }
183
187
 
@@ -17,6 +17,8 @@ export interface PackageEntry {
17
17
  entryFile: string;
18
18
  /** Absolute directory used as the base for subpath computation. Typically dirname(entryFile). */
19
19
  pkgDir: string;
20
+ /** Preserve concrete file paths for package exports that do not support extensionless deep imports. */
21
+ preserveFilePath?: boolean;
20
22
  }
21
23
 
22
24
  export interface BarrelAnalyzerOptions {
@@ -162,6 +164,7 @@ export class BarrelAnalyzer {
162
164
  #subpathFor(pkg: PackageEntry, absFile: string): string | null {
163
165
  const rel = path.relative(pkg.pkgDir, absFile);
164
166
  if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) return null;
167
+ if (pkg.preserveFilePath) return `${pkg.pkgName}/${rel.split(path.sep).join("/")}`;
165
168
  const noExt = stripKnownExt(rel);
166
169
 
167
170
  const tail = collapseIndex(noExt);
@@ -19,7 +19,7 @@ export interface BarrelImportsPluginOptions {
19
19
 
20
20
  export const createBarrelImportsPlugin = async (
21
21
  app: App,
22
- { skipPath = (p) => p.includes("node_modules"), pipeAfter }: BarrelImportsPluginOptions = {},
22
+ { skipPath = defaultSkipPath, pipeAfter }: BarrelImportsPluginOptions = {},
23
23
  ): Promise<BunPlugin> => {
24
24
  const akanConfig = await app.getConfig();
25
25
  const barrels = [...new Set(akanConfig.barrelImports)].filter(Boolean);
@@ -31,40 +31,46 @@ export const createBarrelImportsPlugin = async (
31
31
  name: "barrel-imports",
32
32
  setup(build) {
33
33
 
34
- build.onLoad({ filter: /^(?!.*[\\/]node_modules[\\/]).*\.(tsx|ts|jsx|js)(\?v=\d+)?$/ }, async (args) => {
35
- const realPath = args.path.replace(/\?v=\d+$/, "");
36
- const loader = loaderFor(realPath);
37
- if (skipPath(realPath)) {
38
- const raw = await Bun.file(realPath).text();
39
- return { contents: raw, loader };
40
- }
34
+ build.onLoad(
35
+ {
36
+ filter:
37
+ /^(?:(?!.*[\\/]node_modules[\\/]).*|.*[\\/]node_modules[\\/]akanjs[\\/].*)\.(tsx|ts|jsx|js)(\?v=\d+)?$/,
38
+ },
39
+ async (args) => {
40
+ const realPath = args.path.replace(/\?v=\d+$/, "");
41
+ const loader = loaderFor(realPath);
42
+ if (skipPath(realPath)) {
43
+ const raw = await Bun.file(realPath).text();
44
+ return { contents: raw, loader };
45
+ }
41
46
 
42
- let source = await Bun.file(realPath).text();
47
+ let source = await Bun.file(realPath).text();
43
48
 
44
- const hasMacroAttr = MACRO_ATTR_RE.test(source);
49
+ const hasMacroAttr = MACRO_ATTR_RE.test(source);
45
50
 
46
- if (!hasMacroAttr && barrels.length > 0) {
47
-
48
- let maybe = false;
49
- for (const b of barrels) {
50
- if (source.includes(b)) {
51
- maybe = true;
52
- break;
51
+ if (!hasMacroAttr && barrels.length > 0) {
52
+
53
+ let maybe = false;
54
+ for (const b of barrels) {
55
+ if (source.includes(b)) {
56
+ maybe = true;
57
+ break;
58
+ }
59
+ }
60
+ if (maybe) {
61
+ const rewritten = await rewriteBarrelImports(source, barrels, analyzer);
62
+ if (rewritten !== null) source = rewritten;
53
63
  }
54
64
  }
55
- if (maybe) {
56
- const rewritten = await rewriteBarrelImports(source, barrels, analyzer);
57
- if (rewritten !== null) source = rewritten;
58
- }
59
- }
60
65
 
61
- if (pipeAfter) {
62
- const piped = await pipeAfter(source, { path: realPath });
63
- if (piped !== null) source = piped;
64
- }
66
+ if (pipeAfter) {
67
+ const piped = await pipeAfter(source, { path: realPath });
68
+ if (piped !== null) source = piped;
69
+ }
65
70
 
66
- return { contents: source, loader };
67
- });
71
+ return { contents: source, loader };
72
+ },
73
+ );
68
74
  },
69
75
  };
70
76
  };
@@ -162,6 +168,10 @@ export const createTsconfigPackageResolver = async (
162
168
 
163
169
  const CANDIDATE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
164
170
 
171
+ const NODE_MODULES_RE = /[\\/]node_modules[\\/]/;
172
+ const AKANJS_NODE_MODULE_RE = /[\\/]node_modules[\\/]akanjs[\\/]/;
173
+ const defaultSkipPath = (absPath: string) => NODE_MODULES_RE.test(absPath) && !AKANJS_NODE_MODULE_RE.test(absPath);
174
+
165
175
  type ExportValue = string | string[] | { [condition: string]: ExportValue | undefined };
166
176
 
167
177
  const resolveNodePackageExport = async (workspaceRoot: string, specifier: string): Promise<PackageEntry | null> => {
@@ -178,13 +188,13 @@ const resolveNodePackageExport = async (workspaceRoot: string, specifier: string
178
188
  main?: string;
179
189
  };
180
190
  const subpath = specifier === packageName ? "." : `.${specifier.slice(packageName.length)}`;
181
- const exported = resolveExportValue(pkgJson.exports?.[subpath]);
191
+ const exported = resolvePackageExport(pkgJson.exports, subpath);
182
192
  const rel = exported ?? (subpath === "." ? (pkgJson.module ?? pkgJson.main ?? "index.js") : null);
183
193
  if (!rel || !rel.startsWith(".")) return null;
184
194
  const entryFile = await resolveFileCandidate(path.resolve(pkgDir, rel));
185
195
  if (!entryFile) return null;
186
196
  const pkgEntryName = specifier;
187
- return { pkgName: pkgEntryName, entryFile, pkgDir: path.dirname(entryFile) };
197
+ return { pkgName: pkgEntryName, entryFile, pkgDir: path.dirname(entryFile), preserveFilePath: true };
188
198
  } catch {
189
199
  return null;
190
200
  }
@@ -207,6 +217,25 @@ const resolveExportValue = (value: ExportValue | undefined): string | null => {
207
217
  return null;
208
218
  };
209
219
 
220
+ const resolvePackageExport = (exportsMap: Record<string, ExportValue> | undefined, subpath: string): string | null => {
221
+ if (!exportsMap) return null;
222
+ const exact = resolveExportValue(exportsMap[subpath]);
223
+ if (exact) return exact;
224
+
225
+ for (const [key, value] of Object.entries(exportsMap)) {
226
+ const starIdx = key.indexOf("*");
227
+ if (starIdx === -1) continue;
228
+ const prefix = key.slice(0, starIdx);
229
+ const suffix = key.slice(starIdx + 1);
230
+ if (!subpath.startsWith(prefix) || !subpath.endsWith(suffix)) continue;
231
+ const wildcard = subpath.slice(prefix.length, subpath.length - suffix.length);
232
+ const resolved = resolveExportValue(value);
233
+ if (resolved) return resolved.replace("*", wildcard);
234
+ }
235
+
236
+ return null;
237
+ };
238
+
210
239
  const getPackageName = (specifier: string): string | null => {
211
240
  const parts = specifier.split("/");
212
241
  if (!parts[0]) return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akanjs",
3
- "version": "2.0.0-rc.0",
3
+ "version": "2.0.0-rc.2",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "types": ["bun-types"]
5
- },
6
- "include": ["**/*.test.ts"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "types": ["bun-types"]
5
- },
6
- "include": ["**/*.test.ts"]
7
- }
@@ -1,10 +0,0 @@
1
- # Add files here to ignore them from prettier formatting
2
-
3
- /dist
4
- /coverage
5
- /node_modules
6
- /dump
7
- /data
8
- /releases
9
- /tmp
10
- **/*.yaml
@@ -1,6 +0,0 @@
1
- {
2
- "trailingComma": "es5",
3
- "singleQuote": false,
4
- "printWidth": 120,
5
- "plugins": ["prettier-plugin-tailwindcss"]
6
- }
@@ -1,9 +0,0 @@
1
- {
2
- "jsc": {
3
- "target": "es2016",
4
- "parser": {
5
- "syntax": "typescript"
6
- }
7
- },
8
- "exclude": [".*\\.spec|test\\.ts$"]
9
- }
@@ -1,37 +0,0 @@
1
- ## Get Started
2
-
3
- Run the code below.
4
-
5
- ```
6
- bun run downloadEnv # Need to register your public key
7
-
8
- bun i -g akanjs
9
-
10
- bun i
11
-
12
- cat <<EOF >> .env
13
- # organization configuration, no need to change
14
- AKAN_PUBLIC_REPO_NAME=<%= repoName %>
15
- AKAN_PUBLIC_SERVE_DOMAIN="<%= serveDomain %>"
16
-
17
- # development branch, debug, develop, main, etc. mainly it changes databases.
18
- AKAN_PUBLIC_ENV=debug
19
-
20
- # local, cloud, edge it changes the connection point of the clients.
21
- AKAN_PUBLIC_OPERATION_MODE=local
22
- # hybrid app specific config, will be depreciated in the future
23
- APP_OPERATION_MODE=local
24
-
25
- # backend service mode, federation, batch, all
26
- SERVER_MODE=federation
27
-
28
- # analyze the bundle size
29
- ANALYZE=false
30
-
31
- # log level, debug, info, warn, error
32
- AKAN_PUBLIC_LOG_LEVEL=debug
33
- EOF
34
-
35
- akan serve-backend <%= appName %>
36
- # or akan serve-frontend <%= appName %>, etc
37
- ```