@richapps/ong 0.1.1 → 0.1.3

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/cli.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { createServer, build } from 'vite';
2
2
  import { resolveWorkspace } from './workspace.js';
3
3
  import { createViteConfig } from './config.js';
4
+ import { prebundleLibs } from './prebundle.js';
4
5
  import { banner, printBuildStats, printHelp, error } from './log.js';
5
6
  function parseArgs(argv) {
6
7
  const args = argv.slice(2);
@@ -37,9 +38,16 @@ function parseArgs(argv) {
37
38
  else if (arg === '--watch' || arg === '-w') {
38
39
  result.watch = true;
39
40
  }
41
+ else if (arg === '--prebundle-libs') {
42
+ result.prebundleLibs = true;
43
+ }
40
44
  else if (arg === '--help' || arg === '-h') {
41
45
  return { command: 'help' };
42
46
  }
47
+ else if (!arg.startsWith('-') && !result.project) {
48
+ // Treat bare positional arg as project (e.g. "ong serve apps/editor")
49
+ result.project = arg;
50
+ }
43
51
  }
44
52
  return result;
45
53
  }
@@ -64,7 +72,10 @@ async function main() {
64
72
  };
65
73
  const buildOpts = resolveWorkspace(cwd, resolveOpts);
66
74
  banner(args.command, buildOpts.projectName, buildOpts.configName);
67
- const viteConfig = createViteConfig(buildOpts);
75
+ let viteConfig = createViteConfig(buildOpts);
76
+ if (args.prebundleLibs && args.command === 'serve') {
77
+ viteConfig = await prebundleLibs(viteConfig, buildOpts);
78
+ }
68
79
  if (args.command === 'serve') {
69
80
  const server = await createServer(viteConfig);
70
81
  await server.listen();
package/dist/config.d.ts CHANGED
@@ -1,5 +1,10 @@
1
- import type { InlineConfig } from 'vite';
1
+ import type { InlineConfig, Alias } from 'vite';
2
2
  import type { ResolvedBuildOptions } from './workspace.js';
3
+ /**
4
+ * Reads tsconfig paths and converts them to Vite resolve aliases.
5
+ * Follows tsconfig "extends" chain to find paths from base configs.
6
+ */
7
+ export declare function resolveTsconfigPaths(tsconfig: string, workspaceRoot: string): Alias[];
3
8
  /**
4
9
  * Creates a Vite InlineConfig from resolved Angular build options.
5
10
  */
package/dist/config.js CHANGED
@@ -1,6 +1,67 @@
1
1
  import { resolve } from 'node:path';
2
+ import { readFileSync, existsSync } from 'node:fs';
2
3
  import { angular } from '@oxc-angular/vite';
3
4
  import { htmlInjectPlugin, assetCopyPlugin } from './plugins.js';
5
+ /**
6
+ * Reads tsconfig paths and converts them to Vite resolve aliases.
7
+ * Follows tsconfig "extends" chain to find paths from base configs.
8
+ */
9
+ export function resolveTsconfigPaths(tsconfig, workspaceRoot) {
10
+ const aliases = [];
11
+ try {
12
+ let configPath = tsconfig;
13
+ let paths;
14
+ let baseUrl = '.';
15
+ // Walk the extends chain to find paths
16
+ while (configPath && !paths) {
17
+ if (!existsSync(configPath))
18
+ break;
19
+ const raw = readFileSync(configPath, 'utf-8');
20
+ // Strip comments (single-line // and multi-line /* */)
21
+ const stripped = raw.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
22
+ const config = JSON.parse(stripped);
23
+ const compilerOptions = config.compilerOptions ?? {};
24
+ if (compilerOptions.paths) {
25
+ paths = compilerOptions.paths;
26
+ baseUrl = compilerOptions.baseUrl ?? '.';
27
+ }
28
+ if (config.extends) {
29
+ configPath = resolve(configPath, '..', config.extends);
30
+ // Add .json if not present
31
+ if (!configPath.endsWith('.json'))
32
+ configPath += '.json';
33
+ }
34
+ else {
35
+ break;
36
+ }
37
+ }
38
+ if (!paths)
39
+ return aliases;
40
+ const baseDir = resolve(workspaceRoot, baseUrl);
41
+ for (const [pattern, targets] of Object.entries(paths)) {
42
+ if (!targets.length)
43
+ continue;
44
+ const target = targets[0];
45
+ if (pattern.endsWith('/*')) {
46
+ // Wildcard mapping: "@scope/lib/*" -> "libs/lib/src/*"
47
+ const prefix = pattern.slice(0, -2);
48
+ const targetDir = resolve(baseDir, target.slice(0, -2));
49
+ aliases.push({ find: new RegExp(`^${escapeRegex(prefix)}/(.*)`), replacement: `${targetDir}/$1` });
50
+ }
51
+ else {
52
+ // Exact mapping: "@scope/lib" -> "libs/lib/src/index.ts"
53
+ aliases.push({ find: pattern, replacement: resolve(baseDir, target) });
54
+ }
55
+ }
56
+ }
57
+ catch {
58
+ // Ignore tsconfig parse errors — fall back to no aliases
59
+ }
60
+ return aliases;
61
+ }
62
+ function escapeRegex(str) {
63
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
64
+ }
4
65
  /**
5
66
  * Creates a Vite InlineConfig from resolved Angular build options.
6
67
  */
@@ -29,8 +90,8 @@ export function createViteConfig(opts) {
29
90
  const cssPreprocessorOptions = {};
30
91
  if (opts.stylePreprocessorOptions.includePaths.length) {
31
92
  const paths = opts.stylePreprocessorOptions.includePaths;
32
- cssPreprocessorOptions.scss = { includePaths: paths };
33
- cssPreprocessorOptions.sass = { includePaths: paths };
93
+ cssPreprocessorOptions.scss = { api: 'modern-compiler', loadPaths: paths };
94
+ cssPreprocessorOptions.sass = { api: 'modern-compiler', loadPaths: paths };
34
95
  cssPreprocessorOptions.less = { paths };
35
96
  }
36
97
  return {
@@ -53,6 +114,7 @@ export function createViteConfig(opts) {
53
114
  ...(Object.keys(opts.define).length ? { define: opts.define } : {}),
54
115
  resolve: {
55
116
  preserveSymlinks: opts.preserveSymlinks,
117
+ alias: resolveTsconfigPaths(opts.tsconfig, workspaceRoot),
56
118
  },
57
119
  css: Object.keys(cssPreprocessorOptions).length
58
120
  ? { preprocessorOptions: cssPreprocessorOptions }
@@ -84,6 +146,9 @@ export function createViteConfig(opts) {
84
146
  open: opts.serve.open,
85
147
  host: opts.serve.host ?? false,
86
148
  watch: opts.poll ? { usePolling: true, interval: opts.poll } : undefined,
149
+ fs: {
150
+ allow: [workspaceRoot],
151
+ },
87
152
  },
88
153
  };
89
154
  }
package/dist/index.d.ts CHANGED
@@ -11,3 +11,4 @@ export { resolveWorkspace, detectWorkspace } from './workspace.js';
11
11
  export type { ResolvedBuildOptions, ResolveOptions, AssetConfig } from './workspace.js';
12
12
  export { createViteConfig } from './config.js';
13
13
  export { htmlInjectPlugin, assetCopyPlugin } from './plugins.js';
14
+ export { prebundleLibs } from './prebundle.js';
package/dist/index.js CHANGED
@@ -10,3 +10,4 @@
10
10
  export { resolveWorkspace, detectWorkspace } from './workspace.js';
11
11
  export { createViteConfig } from './config.js';
12
12
  export { htmlInjectPlugin, assetCopyPlugin } from './plugins.js';
13
+ export { prebundleLibs } from './prebundle.js';
package/dist/log.js CHANGED
@@ -80,6 +80,7 @@ export function printHelp() {
80
80
  -o, --open Open browser on start
81
81
  --host <host> Dev server host
82
82
  -w, --watch Rebuild on file changes (build mode)
83
+ --prebundle-libs Pre-bundle local libs to resolve circular deps
83
84
 
84
85
  ${c.bold}Workspace support:${c.reset}
85
86
  angular.json Standard Angular CLI workspace
package/dist/plugins.js CHANGED
@@ -43,17 +43,16 @@ export function htmlInjectPlugin(opts) {
43
43
  }
44
44
  // Inject polyfills before the entry point
45
45
  if (opts.polyfills.length) {
46
- const tags = opts.polyfills
46
+ const imports = opts.polyfills
47
47
  .map(p => {
48
- // Bare specifiers (e.g. "zone.js") become imports, paths become src refs
49
- if (p.startsWith('.') || p.startsWith('/')) {
50
- const ref = '/' + relative(viteRoot, resolve(opts.workspaceRoot, p));
51
- return ` <script type="module" src="${ref}"${crossOriginAttr}></script>`;
52
- }
53
- return ` <script type="module">import '${p}';</script>`;
48
+ // Check if it's a file path (relative to workspace root or absolute)
49
+ const resolved = resolve(opts.workspaceRoot, p);
50
+ const isFile = p.startsWith('.') || p.startsWith('/') || existsSync(resolved);
51
+ // Use absolute path for file imports so Vite can resolve them regardless of root
52
+ return isFile ? `import '${resolved}';` : `import '${p}';`;
54
53
  })
55
54
  .join('\n');
56
- result = result.replace('</body>', `${tags}\n</body>`);
55
+ result = result.replace('</body>', ` <script type="module">\n${imports}\n</script>\n</body>`);
57
56
  }
58
57
  // Inject entry point if not already present
59
58
  const hasEntry = html.includes(`src="${browserRelative}"`) ||
@@ -0,0 +1,8 @@
1
+ import { type InlineConfig } from 'vite';
2
+ import type { ResolvedBuildOptions } from './workspace.js';
3
+ /**
4
+ * Pre-bundles local workspace libraries using rolldown + OXC.
5
+ * This resolves circular dependencies within libs by bundling each
6
+ * into a single ESM file before the Vite dev server starts.
7
+ */
8
+ export declare function prebundleLibs(viteConfig: InlineConfig, opts: ResolvedBuildOptions): Promise<InlineConfig>;
@@ -0,0 +1,113 @@
1
+ import { join } from 'node:path';
2
+ import { mkdirSync, rmSync } from 'node:fs';
3
+ import { build } from 'vite';
4
+ import { angular } from '@oxc-angular/vite';
5
+ import { resolveTsconfigPaths } from './config.js';
6
+ import { c } from './log.js';
7
+ /**
8
+ * Pre-bundles local workspace libraries using rolldown + OXC.
9
+ * This resolves circular dependencies within libs by bundling each
10
+ * into a single ESM file before the Vite dev server starts.
11
+ */
12
+ export async function prebundleLibs(viteConfig, opts) {
13
+ const aliases = resolveTsconfigPaths(opts.tsconfig, opts.workspaceRoot);
14
+ // Find aliases pointing to local workspace files (not node_modules)
15
+ const localLibs = [];
16
+ for (const alias of aliases) {
17
+ // Only handle exact-match aliases (string find), not wildcards (RegExp)
18
+ if (typeof alias.find !== 'string')
19
+ continue;
20
+ const target = alias.replacement;
21
+ // Must be inside workspace and not in node_modules
22
+ if (!target.startsWith(opts.workspaceRoot))
23
+ continue;
24
+ if (target.includes('node_modules'))
25
+ continue;
26
+ localLibs.push({
27
+ find: alias.find,
28
+ entry: target,
29
+ fileName: alias.find.replace(/[@/]/g, '_').replace(/^_+/, ''),
30
+ });
31
+ }
32
+ if (localLibs.length === 0)
33
+ return viteConfig;
34
+ const start = Date.now();
35
+ console.log(` ${c.cyan}Pre-bundling ${localLibs.length} local lib${localLibs.length > 1 ? 's' : ''}...${c.reset}`);
36
+ // Output inside workspace node_modules so Vite can resolve bare imports
37
+ const tempDir = join(opts.workspaceRoot, 'node_modules', '.ong-prebundle');
38
+ rmSync(tempDir, { recursive: true, force: true });
39
+ mkdirSync(tempDir, { recursive: true });
40
+ // Bundle each lib in parallel
41
+ const results = await Promise.allSettled(localLibs.map(lib => bundleLib(lib, tempDir, aliases, opts, viteConfig)));
42
+ // Collect successfully bundled libs
43
+ const bundled = new Map();
44
+ results.forEach((result, i) => {
45
+ if (result.status === 'fulfilled') {
46
+ bundled.set(localLibs[i].find, join(tempDir, `${localLibs[i].fileName}.mjs`));
47
+ }
48
+ else {
49
+ console.log(` ${c.yellow}Warning: failed to pre-bundle ${localLibs[i].find}${c.reset}`);
50
+ console.log(` ${c.dim}${result.reason?.message ?? result.reason}${c.reset}`);
51
+ }
52
+ });
53
+ if (bundled.size === 0)
54
+ return viteConfig;
55
+ const duration = Date.now() - start;
56
+ const names = [...bundled.keys()].join(', ');
57
+ console.log(` ${c.green}Pre-bundled in ${duration}ms:${c.reset} ${c.dim}${names}${c.reset}`);
58
+ console.log();
59
+ // Rewrite aliases: bundled libs point to the temp output
60
+ const newAliases = aliases.map(alias => {
61
+ if (typeof alias.find === 'string' && bundled.has(alias.find)) {
62
+ return { find: alias.find, replacement: bundled.get(alias.find) };
63
+ }
64
+ return alias;
65
+ });
66
+ return {
67
+ ...viteConfig,
68
+ resolve: {
69
+ ...(viteConfig.resolve ?? {}),
70
+ alias: newAliases,
71
+ },
72
+ };
73
+ }
74
+ async function bundleLib(lib, outDir, allAliases, opts, parentConfig) {
75
+ await build({
76
+ configFile: false,
77
+ root: opts.workspaceRoot,
78
+ logLevel: 'warn',
79
+ plugins: [
80
+ ...angular({
81
+ tsconfig: opts.tsconfig,
82
+ sourceMap: opts.sourceMap,
83
+ workspaceRoot: opts.workspaceRoot,
84
+ }),
85
+ ],
86
+ resolve: {
87
+ // All tsconfig aliases so inter-lib imports resolve
88
+ alias: allAliases,
89
+ },
90
+ // Pass through same CSS config for SCSS/Less resolution
91
+ css: parentConfig.css,
92
+ build: {
93
+ lib: {
94
+ entry: lib.entry,
95
+ formats: ['es'],
96
+ fileName: lib.fileName,
97
+ },
98
+ outDir,
99
+ emptyOutDir: false,
100
+ sourcemap: opts.sourceMap,
101
+ minify: false,
102
+ rollupOptions: {
103
+ external: (id) => {
104
+ // Bundle relative/absolute imports (internal to the lib)
105
+ if (id.startsWith('.') || id.startsWith('/'))
106
+ return false;
107
+ // Externalize everything else (npm packages, other local lib aliases)
108
+ return true;
109
+ },
110
+ },
111
+ },
112
+ });
113
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@richapps/ong",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Angular CLI powered by Vite + OXC — blazing fast drop-in replacement for ng serve/build",
5
5
  "keywords": [
6
6
  "angular",