angular-grab 0.1.1 → 0.1.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/README.md +215 -0
- package/examples/angular-19-app/.editorconfig +17 -0
- package/examples/angular-19-app/.vscode/extensions.json +4 -0
- package/examples/angular-19-app/.vscode/launch.json +20 -0
- package/examples/angular-19-app/.vscode/mcp.json +9 -0
- package/examples/angular-19-app/.vscode/tasks.json +42 -0
- package/examples/angular-19-app/README.md +59 -0
- package/examples/angular-19-app/angular.json +74 -0
- package/examples/angular-19-app/package.json +44 -0
- package/examples/angular-19-app/public/favicon.ico +0 -0
- package/examples/angular-19-app/src/app/app.config.ts +13 -0
- package/examples/angular-19-app/src/app/app.css +37 -0
- package/examples/angular-19-app/src/app/app.html +25 -0
- package/examples/angular-19-app/src/app/app.routes.ts +3 -0
- package/examples/angular-19-app/src/app/app.spec.ts +23 -0
- package/examples/angular-19-app/src/app/app.ts +12 -0
- package/examples/angular-19-app/src/app/button/button.component.ts +25 -0
- package/examples/angular-19-app/src/app/card/card.component.ts +33 -0
- package/examples/angular-19-app/src/app/header/header.component.ts +31 -0
- package/examples/angular-19-app/src/app/popover/popover.component.ts +133 -0
- package/examples/angular-19-app/src/index.html +13 -0
- package/examples/angular-19-app/src/main.ts +6 -0
- package/examples/angular-19-app/src/styles.css +1 -0
- package/examples/angular-19-app/tsconfig.app.json +15 -0
- package/examples/angular-19-app/tsconfig.json +33 -0
- package/examples/angular-19-app/tsconfig.spec.json +15 -0
- package/package.json +14 -111
- package/packages/angular-grab/package.json +96 -0
- package/packages/angular-grab/src/angular/__tests__/context-builder.test.ts +216 -0
- package/packages/angular-grab/src/angular/angular-grab.service.ts +62 -0
- package/packages/angular-grab/src/angular/index.ts +13 -0
- package/packages/angular-grab/src/angular/provide-angular-grab.ts +22 -0
- package/packages/angular-grab/src/angular/resolvers/component-resolver.ts +71 -0
- package/packages/angular-grab/src/angular/resolvers/context-builder.ts +86 -0
- package/packages/angular-grab/src/angular/resolvers/ng-utils.ts +14 -0
- package/packages/angular-grab/src/angular/resolvers/source-resolver.ts +61 -0
- package/packages/angular-grab/src/builder/__tests__/builder.test.ts +72 -0
- package/packages/angular-grab/src/builder/builders/application/index.ts +13 -0
- package/packages/angular-grab/src/builder/builders/dev-server/index.ts +9 -0
- package/packages/angular-grab/src/builder/index.ts +3 -0
- package/packages/angular-grab/src/cli/__tests__/cli.test.ts +239 -0
- package/packages/angular-grab/src/cli/commands/init.ts +106 -0
- package/packages/angular-grab/src/cli/index.ts +15 -0
- package/packages/angular-grab/src/cli/utils/detect-project.ts +78 -0
- package/packages/angular-grab/src/cli/utils/modify-angular-json.ts +42 -0
- package/packages/angular-grab/src/cli/utils/modify-app-config.ts +42 -0
- package/packages/angular-grab/src/core/__tests__/generate-snippet.test.ts +149 -0
- package/packages/angular-grab/src/core/__tests__/plugin-registry.test.ts +286 -0
- package/packages/angular-grab/src/core/__tests__/store.test.ts +118 -0
- package/packages/angular-grab/src/core/__tests__/utils.test.ts +85 -0
- package/packages/angular-grab/src/core/clipboard/copy.ts +104 -0
- package/packages/angular-grab/src/core/clipboard/generate-snippet.ts +38 -0
- package/packages/angular-grab/src/core/constants.ts +10 -0
- package/packages/angular-grab/src/core/grab.ts +596 -0
- package/packages/angular-grab/src/core/index.global.ts +13 -0
- package/packages/angular-grab/src/core/index.ts +19 -0
- package/packages/angular-grab/src/core/keyboard/keyboard-handler.ts +163 -0
- package/packages/angular-grab/src/core/overlay/crosshair.ts +107 -0
- package/packages/angular-grab/src/core/overlay/freeze-overlay.ts +239 -0
- package/packages/angular-grab/src/core/overlay/overlay-renderer.ts +180 -0
- package/packages/angular-grab/src/core/overlay/select-feedback.ts +108 -0
- package/packages/angular-grab/src/core/overlay/toast.ts +175 -0
- package/packages/angular-grab/src/core/picker/element-picker.ts +114 -0
- package/packages/angular-grab/src/core/plugins/plugin-registry.ts +83 -0
- package/packages/angular-grab/src/core/store.ts +52 -0
- package/packages/angular-grab/src/core/toolbar/actions-menu.ts +178 -0
- package/packages/angular-grab/src/core/toolbar/comment-popover.ts +235 -0
- package/packages/angular-grab/src/core/toolbar/copy-actions.ts +98 -0
- package/packages/angular-grab/src/core/toolbar/history-popover.ts +245 -0
- package/packages/angular-grab/src/core/toolbar/theme-manager.ts +188 -0
- package/packages/angular-grab/src/core/toolbar/toolbar-icons.ts +29 -0
- package/packages/angular-grab/src/core/toolbar/toolbar-renderer.ts +239 -0
- package/packages/angular-grab/src/core/types.ts +139 -0
- package/packages/angular-grab/src/core/utils.ts +16 -0
- package/packages/angular-grab/src/esbuild-plugin/__tests__/transform.test.ts +174 -0
- package/packages/angular-grab/src/esbuild-plugin/index.ts +3 -0
- package/packages/angular-grab/src/esbuild-plugin/plugin.ts +29 -0
- package/packages/angular-grab/src/esbuild-plugin/scan.ts +105 -0
- package/packages/angular-grab/src/esbuild-plugin/transform.ts +152 -0
- package/packages/angular-grab/src/vite-plugin/__tests__/plugin.test.ts +84 -0
- package/packages/angular-grab/src/vite-plugin/index.ts +19 -0
- package/packages/angular-grab/src/webpack-plugin/__tests__/plugin.test.ts +72 -0
- package/packages/angular-grab/src/webpack-plugin/index.ts +2 -0
- package/packages/angular-grab/src/webpack-plugin/loader.ts +15 -0
- package/packages/angular-grab/src/webpack-plugin/plugin.ts +20 -0
- package/packages/angular-grab/tsconfig.json +15 -0
- package/packages/angular-grab/tsup.config.ts +119 -0
- package/pnpm-workspace.yaml +3 -0
- package/turbo.json +21 -0
- package/dist/angular/index.d.ts +0 -151
- package/dist/angular/index.js +0 -2811
- package/dist/angular/index.js.map +0 -1
- package/dist/builder/builders/application/index.js +0 -143
- package/dist/builder/builders/application/index.js.map +0 -1
- package/dist/builder/builders/dev-server/index.js +0 -139
- package/dist/builder/builders/dev-server/index.js.map +0 -1
- package/dist/builder/index.js +0 -2
- package/dist/builder/index.js.map +0 -1
- package/dist/builder/package.json +0 -1
- package/dist/cli/index.js +0 -223
- package/dist/cli/index.js.map +0 -1
- package/dist/core/index.cjs +0 -2589
- package/dist/core/index.cjs.map +0 -1
- package/dist/core/index.d.cts +0 -139
- package/dist/core/index.d.ts +0 -139
- package/dist/core/index.global.js +0 -542
- package/dist/core/index.js +0 -2560
- package/dist/core/index.js.map +0 -1
- package/dist/esbuild-plugin/index.cjs +0 -239
- package/dist/esbuild-plugin/index.cjs.map +0 -1
- package/dist/esbuild-plugin/index.d.cts +0 -26
- package/dist/esbuild-plugin/index.d.ts +0 -26
- package/dist/esbuild-plugin/index.js +0 -200
- package/dist/esbuild-plugin/index.js.map +0 -1
- package/dist/vite-plugin/index.d.ts +0 -7
- package/dist/vite-plugin/index.js +0 -128
- package/dist/vite-plugin/index.js.map +0 -1
- package/dist/webpack-plugin/index.cjs +0 -54
- package/dist/webpack-plugin/index.cjs.map +0 -1
- package/dist/webpack-plugin/index.d.cts +0 -5
- package/dist/webpack-plugin/index.d.ts +0 -5
- package/dist/webpack-plugin/index.js +0 -23
- package/dist/webpack-plugin/index.js.map +0 -1
- package/dist/webpack-plugin/loader.cjs +0 -155
- package/dist/webpack-plugin/loader.cjs.map +0 -1
- package/dist/webpack-plugin/loader.d.cts +0 -3
- package/dist/webpack-plugin/loader.d.ts +0 -3
- package/dist/webpack-plugin/loader.js +0 -122
- package/dist/webpack-plugin/loader.js.map +0 -1
- /package/{builders.json → packages/angular-grab/builders.json} +0 -0
- /package/{dist → packages/angular-grab/src}/builder/builders/application/schema.json +0 -0
- /package/{dist → packages/angular-grab/src}/builder/builders/dev-server/schema.json +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/esbuild-plugin/index.ts","../../src/esbuild-plugin/scan.ts","../../src/esbuild-plugin/plugin.ts","../../src/esbuild-plugin/transform.ts"],"sourcesContent":["export { angularGrabEsbuildPlugin } from './plugin';\nexport { transformAngularComponent, type TransformResult } from './transform';\nexport { scanComponentSources, type SourceMap, type ComponentSourceInfo } from './scan';\n","import ts from 'typescript';\nimport path from 'path';\nimport fs from 'fs';\n\nexport interface ComponentSourceInfo {\n file: string;\n line: number;\n}\n\nexport type SourceMap = Record<string, ComponentSourceInfo>;\n\n/**\n * Scans the project for `.component.ts` files and extracts component\n * class names with their source locations. Returns a map of\n * `{ ComponentName: { file, line } }`.\n */\nexport function scanComponentSources(rootDir: string): SourceMap {\n const sourceMap: SourceMap = {};\n const files = findComponentFiles(rootDir);\n\n for (const filePath of files) {\n const content = fs.readFileSync(filePath, 'utf8');\n if (!content.includes('@Component')) continue;\n\n const relativePath = path.relative(rootDir, filePath).replace(/\\\\/g, '/');\n const components = extractComponents(content, filePath);\n\n for (const comp of components) {\n sourceMap[comp.name] = { file: relativePath, line: comp.line };\n }\n }\n\n return sourceMap;\n}\n\ninterface ComponentInfo {\n name: string;\n line: number;\n}\n\nfunction extractComponents(code: string, filePath: string): ComponentInfo[] {\n const sourceFile = ts.createSourceFile(\n filePath,\n code,\n ts.ScriptTarget.Latest,\n true,\n ts.ScriptKind.TS,\n );\n\n const results: ComponentInfo[] = [];\n\n function visit(node: ts.Node): void {\n if (ts.isClassDeclaration(node) && node.name) {\n const decorators = ts.getDecorators(node);\n if (!decorators) return;\n\n const hasComponent = decorators.some((d) => {\n if (!ts.isCallExpression(d.expression)) return false;\n if (!ts.isIdentifier(d.expression.expression)) return false;\n return d.expression.expression.text === 'Component';\n });\n\n if (hasComponent) {\n const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());\n results.push({ name: node.name.text, line: line + 1 });\n }\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return results;\n}\n\nfunction findComponentFiles(rootDir: string): string[] {\n const results: string[] = [];\n const srcDir = path.join(rootDir, 'src');\n\n if (!fs.existsSync(srcDir)) return results;\n\n walk(srcDir, results);\n return results;\n}\n\nfunction walk(dir: string, results: string[]): void {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue;\n\n const fullPath = path.join(dir, entry.name);\n\n if (entry.isDirectory()) {\n walk(fullPath, results);\n } else if (entry.name.endsWith('.component.ts')) {\n results.push(fullPath);\n }\n }\n}\n","import type { Plugin } from 'esbuild';\nimport { scanComponentSources } from './scan';\n\nexport function angularGrabEsbuildPlugin(options?: {\n rootDir?: string;\n /** Set to false to disable the transform (e.g., in production). Default: true */\n enabled?: boolean;\n}): Plugin {\n return {\n name: 'angular-grab',\n setup(build) {\n if (options?.enabled === false) return;\n\n const rootDir = options?.rootDir || process.cwd();\n const sourceMap = scanComponentSources(rootDir);\n\n if (Object.keys(sourceMap).length === 0) return;\n\n // Inject the source map as a global variable via banner\n const json = JSON.stringify(sourceMap);\n const banner = build.initialOptions.banner || {};\n const existing = typeof banner === 'object' ? (banner.js || '') : '';\n build.initialOptions.banner = {\n ...(typeof banner === 'object' ? banner : {}),\n js: `globalThis.__ANGULAR_GRAB_SOURCE_MAP__=${json};${existing}`,\n };\n },\n };\n}\n","import ts from 'typescript';\nimport path from 'path';\n\nexport interface TransformResult {\n code: string;\n}\n\ninterface Edit {\n pos: number;\n end: number;\n text: string;\n}\n\nexport function transformAngularComponent(\n code: string,\n filePath: string,\n rootDir: string,\n): TransformResult | null {\n const sourceFile = ts.createSourceFile(\n filePath,\n code,\n ts.ScriptTarget.Latest,\n true,\n ts.ScriptKind.TS,\n );\n\n const relativePath = path.relative(rootDir, filePath).replace(/\\\\/g, '/').replace(/'/g, \"\\\\'\");\n const edits: Edit[] = [];\n\n visitNode(sourceFile);\n\n if (edits.length === 0) return null;\n\n // Apply edits in reverse order to preserve positions\n edits.sort((a, b) => b.pos - a.pos);\n\n let result = code;\n for (const edit of edits) {\n result = result.slice(0, edit.pos) + edit.text + result.slice(edit.end);\n }\n\n return { code: result };\n\n function visitNode(node: ts.Node): void {\n if (ts.isClassDeclaration(node)) {\n processClassDeclaration(node);\n }\n ts.forEachChild(node, visitNode);\n }\n\n function processClassDeclaration(classNode: ts.ClassDeclaration): void {\n const decorators = ts.getDecorators(classNode);\n if (!decorators) return;\n\n for (const decorator of decorators) {\n if (!ts.isCallExpression(decorator.expression)) continue;\n\n const expr = decorator.expression;\n if (!ts.isIdentifier(expr.expression)) continue;\n if (expr.expression.text !== 'Component') continue;\n\n processComponentDecorator(expr);\n }\n }\n\n function processComponentDecorator(callExpr: ts.CallExpression): void {\n if (callExpr.arguments.length === 0) {\n // Empty decorator call: @Component() — add host arg\n const line = sourceFile.getLineAndCharacterOfPosition(callExpr.getStart()).line;\n const sourceAttr = `'data-ng-source': '${relativePath}:${line}:0'`;\n const insertText = `{ host: { ${sourceAttr} } }`;\n\n const openParen = callExpr.getStart() + callExpr.expression.getText().length;\n // Find the actual position of the opening paren\n const parenPos = code.indexOf('(', openParen);\n const closeParenPos = code.indexOf(')', parenPos);\n\n edits.push({\n pos: parenPos + 1,\n end: closeParenPos,\n text: insertText,\n });\n return;\n }\n\n const arg = callExpr.arguments[0];\n if (!ts.isObjectLiteralExpression(arg)) return;\n\n const line = sourceFile.getLineAndCharacterOfPosition(callExpr.getStart()).line;\n const sourceAttr = `'data-ng-source': '${relativePath}:${line}:0'`;\n\n // Check if host property already exists\n const hostProp = arg.properties.find(\n (p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === 'host',\n ) as ts.PropertyAssignment | undefined;\n\n if (hostProp) {\n // Host property exists — inject into it\n if (!ts.isObjectLiteralExpression(hostProp.initializer)) return;\n\n const hostObj = hostProp.initializer;\n\n // Check for existing data-ng-source (idempotent)\n const hasExisting = hostObj.properties.some(\n (p) =>\n ts.isPropertyAssignment(p) &&\n ((ts.isStringLiteral(p.name) && p.name.text === 'data-ng-source') ||\n (ts.isIdentifier(p.name) && p.name.text === 'data-ng-source')),\n );\n\n if (hasExisting) return;\n\n // Insert into existing host object\n if (hostObj.properties.length > 0) {\n const lastProp = hostObj.properties[hostObj.properties.length - 1];\n edits.push({\n pos: lastProp.getEnd(),\n end: lastProp.getEnd(),\n text: `, ${sourceAttr}`,\n });\n } else {\n // Empty host object: host: {}\n const openBrace = hostObj.getStart();\n const closeBrace = hostObj.getEnd();\n edits.push({\n pos: openBrace + 1,\n end: closeBrace - 1,\n text: ` ${sourceAttr} `,\n });\n }\n } else {\n // No host property — add one\n if (arg.properties.length > 0) {\n const lastProp = arg.properties[arg.properties.length - 1];\n edits.push({\n pos: lastProp.getEnd(),\n end: lastProp.getEnd(),\n text: `, host: { ${sourceAttr} }`,\n });\n } else {\n // Empty object literal: @Component({})\n const openBrace = arg.getStart();\n const closeBrace = arg.getEnd();\n edits.push({\n pos: openBrace + 1,\n end: closeBrace - 1,\n text: ` host: { ${sourceAttr} } `,\n });\n }\n }\n }\n}\n"],"mappings":"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","names":["fs","path","ts","import_typescript","import_path","ts","path","line","sourceAttr"]}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Plugin } from 'esbuild';
|
|
2
|
-
|
|
3
|
-
declare function angularGrabEsbuildPlugin(options?: {
|
|
4
|
-
rootDir?: string;
|
|
5
|
-
/** Set to false to disable the transform (e.g., in production). Default: true */
|
|
6
|
-
enabled?: boolean;
|
|
7
|
-
}): Plugin;
|
|
8
|
-
|
|
9
|
-
interface TransformResult {
|
|
10
|
-
code: string;
|
|
11
|
-
}
|
|
12
|
-
declare function transformAngularComponent(code: string, filePath: string, rootDir: string): TransformResult | null;
|
|
13
|
-
|
|
14
|
-
interface ComponentSourceInfo {
|
|
15
|
-
file: string;
|
|
16
|
-
line: number;
|
|
17
|
-
}
|
|
18
|
-
type SourceMap = Record<string, ComponentSourceInfo>;
|
|
19
|
-
/**
|
|
20
|
-
* Scans the project for `.component.ts` files and extracts component
|
|
21
|
-
* class names with their source locations. Returns a map of
|
|
22
|
-
* `{ ComponentName: { file, line } }`.
|
|
23
|
-
*/
|
|
24
|
-
declare function scanComponentSources(rootDir: string): SourceMap;
|
|
25
|
-
|
|
26
|
-
export { type ComponentSourceInfo, type SourceMap, type TransformResult, angularGrabEsbuildPlugin, scanComponentSources, transformAngularComponent };
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Plugin } from 'esbuild';
|
|
2
|
-
|
|
3
|
-
declare function angularGrabEsbuildPlugin(options?: {
|
|
4
|
-
rootDir?: string;
|
|
5
|
-
/** Set to false to disable the transform (e.g., in production). Default: true */
|
|
6
|
-
enabled?: boolean;
|
|
7
|
-
}): Plugin;
|
|
8
|
-
|
|
9
|
-
interface TransformResult {
|
|
10
|
-
code: string;
|
|
11
|
-
}
|
|
12
|
-
declare function transformAngularComponent(code: string, filePath: string, rootDir: string): TransformResult | null;
|
|
13
|
-
|
|
14
|
-
interface ComponentSourceInfo {
|
|
15
|
-
file: string;
|
|
16
|
-
line: number;
|
|
17
|
-
}
|
|
18
|
-
type SourceMap = Record<string, ComponentSourceInfo>;
|
|
19
|
-
/**
|
|
20
|
-
* Scans the project for `.component.ts` files and extracts component
|
|
21
|
-
* class names with their source locations. Returns a map of
|
|
22
|
-
* `{ ComponentName: { file, line } }`.
|
|
23
|
-
*/
|
|
24
|
-
declare function scanComponentSources(rootDir: string): SourceMap;
|
|
25
|
-
|
|
26
|
-
export { type ComponentSourceInfo, type SourceMap, type TransformResult, angularGrabEsbuildPlugin, scanComponentSources, transformAngularComponent };
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
// src/esbuild-plugin/scan.ts
|
|
2
|
-
import ts from "typescript";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
function scanComponentSources(rootDir) {
|
|
6
|
-
const sourceMap = {};
|
|
7
|
-
const files = findComponentFiles(rootDir);
|
|
8
|
-
for (const filePath of files) {
|
|
9
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
10
|
-
if (!content.includes("@Component")) continue;
|
|
11
|
-
const relativePath = path.relative(rootDir, filePath).replace(/\\/g, "/");
|
|
12
|
-
const components = extractComponents(content, filePath);
|
|
13
|
-
for (const comp of components) {
|
|
14
|
-
sourceMap[comp.name] = { file: relativePath, line: comp.line };
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return sourceMap;
|
|
18
|
-
}
|
|
19
|
-
function extractComponents(code, filePath) {
|
|
20
|
-
const sourceFile = ts.createSourceFile(
|
|
21
|
-
filePath,
|
|
22
|
-
code,
|
|
23
|
-
ts.ScriptTarget.Latest,
|
|
24
|
-
true,
|
|
25
|
-
ts.ScriptKind.TS
|
|
26
|
-
);
|
|
27
|
-
const results = [];
|
|
28
|
-
function visit(node) {
|
|
29
|
-
if (ts.isClassDeclaration(node) && node.name) {
|
|
30
|
-
const decorators = ts.getDecorators(node);
|
|
31
|
-
if (!decorators) return;
|
|
32
|
-
const hasComponent = decorators.some((d) => {
|
|
33
|
-
if (!ts.isCallExpression(d.expression)) return false;
|
|
34
|
-
if (!ts.isIdentifier(d.expression.expression)) return false;
|
|
35
|
-
return d.expression.expression.text === "Component";
|
|
36
|
-
});
|
|
37
|
-
if (hasComponent) {
|
|
38
|
-
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
39
|
-
results.push({ name: node.name.text, line: line + 1 });
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
ts.forEachChild(node, visit);
|
|
43
|
-
}
|
|
44
|
-
visit(sourceFile);
|
|
45
|
-
return results;
|
|
46
|
-
}
|
|
47
|
-
function findComponentFiles(rootDir) {
|
|
48
|
-
const results = [];
|
|
49
|
-
const srcDir = path.join(rootDir, "src");
|
|
50
|
-
if (!fs.existsSync(srcDir)) return results;
|
|
51
|
-
walk(srcDir, results);
|
|
52
|
-
return results;
|
|
53
|
-
}
|
|
54
|
-
function walk(dir, results) {
|
|
55
|
-
let entries;
|
|
56
|
-
try {
|
|
57
|
-
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
58
|
-
} catch {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
for (const entry of entries) {
|
|
62
|
-
if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
|
|
63
|
-
const fullPath = path.join(dir, entry.name);
|
|
64
|
-
if (entry.isDirectory()) {
|
|
65
|
-
walk(fullPath, results);
|
|
66
|
-
} else if (entry.name.endsWith(".component.ts")) {
|
|
67
|
-
results.push(fullPath);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// src/esbuild-plugin/plugin.ts
|
|
73
|
-
function angularGrabEsbuildPlugin(options) {
|
|
74
|
-
return {
|
|
75
|
-
name: "angular-grab",
|
|
76
|
-
setup(build) {
|
|
77
|
-
if (options?.enabled === false) return;
|
|
78
|
-
const rootDir = options?.rootDir || process.cwd();
|
|
79
|
-
const sourceMap = scanComponentSources(rootDir);
|
|
80
|
-
if (Object.keys(sourceMap).length === 0) return;
|
|
81
|
-
const json = JSON.stringify(sourceMap);
|
|
82
|
-
const banner = build.initialOptions.banner || {};
|
|
83
|
-
const existing = typeof banner === "object" ? banner.js || "" : "";
|
|
84
|
-
build.initialOptions.banner = {
|
|
85
|
-
...typeof banner === "object" ? banner : {},
|
|
86
|
-
js: `globalThis.__ANGULAR_GRAB_SOURCE_MAP__=${json};${existing}`
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// src/esbuild-plugin/transform.ts
|
|
93
|
-
import ts2 from "typescript";
|
|
94
|
-
import path2 from "path";
|
|
95
|
-
function transformAngularComponent(code, filePath, rootDir) {
|
|
96
|
-
const sourceFile = ts2.createSourceFile(
|
|
97
|
-
filePath,
|
|
98
|
-
code,
|
|
99
|
-
ts2.ScriptTarget.Latest,
|
|
100
|
-
true,
|
|
101
|
-
ts2.ScriptKind.TS
|
|
102
|
-
);
|
|
103
|
-
const relativePath = path2.relative(rootDir, filePath).replace(/\\/g, "/").replace(/'/g, "\\'");
|
|
104
|
-
const edits = [];
|
|
105
|
-
visitNode(sourceFile);
|
|
106
|
-
if (edits.length === 0) return null;
|
|
107
|
-
edits.sort((a, b) => b.pos - a.pos);
|
|
108
|
-
let result = code;
|
|
109
|
-
for (const edit of edits) {
|
|
110
|
-
result = result.slice(0, edit.pos) + edit.text + result.slice(edit.end);
|
|
111
|
-
}
|
|
112
|
-
return { code: result };
|
|
113
|
-
function visitNode(node) {
|
|
114
|
-
if (ts2.isClassDeclaration(node)) {
|
|
115
|
-
processClassDeclaration(node);
|
|
116
|
-
}
|
|
117
|
-
ts2.forEachChild(node, visitNode);
|
|
118
|
-
}
|
|
119
|
-
function processClassDeclaration(classNode) {
|
|
120
|
-
const decorators = ts2.getDecorators(classNode);
|
|
121
|
-
if (!decorators) return;
|
|
122
|
-
for (const decorator of decorators) {
|
|
123
|
-
if (!ts2.isCallExpression(decorator.expression)) continue;
|
|
124
|
-
const expr = decorator.expression;
|
|
125
|
-
if (!ts2.isIdentifier(expr.expression)) continue;
|
|
126
|
-
if (expr.expression.text !== "Component") continue;
|
|
127
|
-
processComponentDecorator(expr);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
function processComponentDecorator(callExpr) {
|
|
131
|
-
if (callExpr.arguments.length === 0) {
|
|
132
|
-
const line2 = sourceFile.getLineAndCharacterOfPosition(callExpr.getStart()).line;
|
|
133
|
-
const sourceAttr2 = `'data-ng-source': '${relativePath}:${line2}:0'`;
|
|
134
|
-
const insertText = `{ host: { ${sourceAttr2} } }`;
|
|
135
|
-
const openParen = callExpr.getStart() + callExpr.expression.getText().length;
|
|
136
|
-
const parenPos = code.indexOf("(", openParen);
|
|
137
|
-
const closeParenPos = code.indexOf(")", parenPos);
|
|
138
|
-
edits.push({
|
|
139
|
-
pos: parenPos + 1,
|
|
140
|
-
end: closeParenPos,
|
|
141
|
-
text: insertText
|
|
142
|
-
});
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
const arg = callExpr.arguments[0];
|
|
146
|
-
if (!ts2.isObjectLiteralExpression(arg)) return;
|
|
147
|
-
const line = sourceFile.getLineAndCharacterOfPosition(callExpr.getStart()).line;
|
|
148
|
-
const sourceAttr = `'data-ng-source': '${relativePath}:${line}:0'`;
|
|
149
|
-
const hostProp = arg.properties.find(
|
|
150
|
-
(p) => ts2.isPropertyAssignment(p) && ts2.isIdentifier(p.name) && p.name.text === "host"
|
|
151
|
-
);
|
|
152
|
-
if (hostProp) {
|
|
153
|
-
if (!ts2.isObjectLiteralExpression(hostProp.initializer)) return;
|
|
154
|
-
const hostObj = hostProp.initializer;
|
|
155
|
-
const hasExisting = hostObj.properties.some(
|
|
156
|
-
(p) => ts2.isPropertyAssignment(p) && (ts2.isStringLiteral(p.name) && p.name.text === "data-ng-source" || ts2.isIdentifier(p.name) && p.name.text === "data-ng-source")
|
|
157
|
-
);
|
|
158
|
-
if (hasExisting) return;
|
|
159
|
-
if (hostObj.properties.length > 0) {
|
|
160
|
-
const lastProp = hostObj.properties[hostObj.properties.length - 1];
|
|
161
|
-
edits.push({
|
|
162
|
-
pos: lastProp.getEnd(),
|
|
163
|
-
end: lastProp.getEnd(),
|
|
164
|
-
text: `, ${sourceAttr}`
|
|
165
|
-
});
|
|
166
|
-
} else {
|
|
167
|
-
const openBrace = hostObj.getStart();
|
|
168
|
-
const closeBrace = hostObj.getEnd();
|
|
169
|
-
edits.push({
|
|
170
|
-
pos: openBrace + 1,
|
|
171
|
-
end: closeBrace - 1,
|
|
172
|
-
text: ` ${sourceAttr} `
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
} else {
|
|
176
|
-
if (arg.properties.length > 0) {
|
|
177
|
-
const lastProp = arg.properties[arg.properties.length - 1];
|
|
178
|
-
edits.push({
|
|
179
|
-
pos: lastProp.getEnd(),
|
|
180
|
-
end: lastProp.getEnd(),
|
|
181
|
-
text: `, host: { ${sourceAttr} }`
|
|
182
|
-
});
|
|
183
|
-
} else {
|
|
184
|
-
const openBrace = arg.getStart();
|
|
185
|
-
const closeBrace = arg.getEnd();
|
|
186
|
-
edits.push({
|
|
187
|
-
pos: openBrace + 1,
|
|
188
|
-
end: closeBrace - 1,
|
|
189
|
-
text: ` host: { ${sourceAttr} } `
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
export {
|
|
196
|
-
angularGrabEsbuildPlugin,
|
|
197
|
-
scanComponentSources,
|
|
198
|
-
transformAngularComponent
|
|
199
|
-
};
|
|
200
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/esbuild-plugin/scan.ts","../../src/esbuild-plugin/plugin.ts","../../src/esbuild-plugin/transform.ts"],"sourcesContent":["import ts from 'typescript';\nimport path from 'path';\nimport fs from 'fs';\n\nexport interface ComponentSourceInfo {\n file: string;\n line: number;\n}\n\nexport type SourceMap = Record<string, ComponentSourceInfo>;\n\n/**\n * Scans the project for `.component.ts` files and extracts component\n * class names with their source locations. Returns a map of\n * `{ ComponentName: { file, line } }`.\n */\nexport function scanComponentSources(rootDir: string): SourceMap {\n const sourceMap: SourceMap = {};\n const files = findComponentFiles(rootDir);\n\n for (const filePath of files) {\n const content = fs.readFileSync(filePath, 'utf8');\n if (!content.includes('@Component')) continue;\n\n const relativePath = path.relative(rootDir, filePath).replace(/\\\\/g, '/');\n const components = extractComponents(content, filePath);\n\n for (const comp of components) {\n sourceMap[comp.name] = { file: relativePath, line: comp.line };\n }\n }\n\n return sourceMap;\n}\n\ninterface ComponentInfo {\n name: string;\n line: number;\n}\n\nfunction extractComponents(code: string, filePath: string): ComponentInfo[] {\n const sourceFile = ts.createSourceFile(\n filePath,\n code,\n ts.ScriptTarget.Latest,\n true,\n ts.ScriptKind.TS,\n );\n\n const results: ComponentInfo[] = [];\n\n function visit(node: ts.Node): void {\n if (ts.isClassDeclaration(node) && node.name) {\n const decorators = ts.getDecorators(node);\n if (!decorators) return;\n\n const hasComponent = decorators.some((d) => {\n if (!ts.isCallExpression(d.expression)) return false;\n if (!ts.isIdentifier(d.expression.expression)) return false;\n return d.expression.expression.text === 'Component';\n });\n\n if (hasComponent) {\n const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());\n results.push({ name: node.name.text, line: line + 1 });\n }\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return results;\n}\n\nfunction findComponentFiles(rootDir: string): string[] {\n const results: string[] = [];\n const srcDir = path.join(rootDir, 'src');\n\n if (!fs.existsSync(srcDir)) return results;\n\n walk(srcDir, results);\n return results;\n}\n\nfunction walk(dir: string, results: string[]): void {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue;\n\n const fullPath = path.join(dir, entry.name);\n\n if (entry.isDirectory()) {\n walk(fullPath, results);\n } else if (entry.name.endsWith('.component.ts')) {\n results.push(fullPath);\n }\n }\n}\n","import type { Plugin } from 'esbuild';\nimport { scanComponentSources } from './scan';\n\nexport function angularGrabEsbuildPlugin(options?: {\n rootDir?: string;\n /** Set to false to disable the transform (e.g., in production). Default: true */\n enabled?: boolean;\n}): Plugin {\n return {\n name: 'angular-grab',\n setup(build) {\n if (options?.enabled === false) return;\n\n const rootDir = options?.rootDir || process.cwd();\n const sourceMap = scanComponentSources(rootDir);\n\n if (Object.keys(sourceMap).length === 0) return;\n\n // Inject the source map as a global variable via banner\n const json = JSON.stringify(sourceMap);\n const banner = build.initialOptions.banner || {};\n const existing = typeof banner === 'object' ? (banner.js || '') : '';\n build.initialOptions.banner = {\n ...(typeof banner === 'object' ? banner : {}),\n js: `globalThis.__ANGULAR_GRAB_SOURCE_MAP__=${json};${existing}`,\n };\n },\n };\n}\n","import ts from 'typescript';\nimport path from 'path';\n\nexport interface TransformResult {\n code: string;\n}\n\ninterface Edit {\n pos: number;\n end: number;\n text: string;\n}\n\nexport function transformAngularComponent(\n code: string,\n filePath: string,\n rootDir: string,\n): TransformResult | null {\n const sourceFile = ts.createSourceFile(\n filePath,\n code,\n ts.ScriptTarget.Latest,\n true,\n ts.ScriptKind.TS,\n );\n\n const relativePath = path.relative(rootDir, filePath).replace(/\\\\/g, '/').replace(/'/g, \"\\\\'\");\n const edits: Edit[] = [];\n\n visitNode(sourceFile);\n\n if (edits.length === 0) return null;\n\n // Apply edits in reverse order to preserve positions\n edits.sort((a, b) => b.pos - a.pos);\n\n let result = code;\n for (const edit of edits) {\n result = result.slice(0, edit.pos) + edit.text + result.slice(edit.end);\n }\n\n return { code: result };\n\n function visitNode(node: ts.Node): void {\n if (ts.isClassDeclaration(node)) {\n processClassDeclaration(node);\n }\n ts.forEachChild(node, visitNode);\n }\n\n function processClassDeclaration(classNode: ts.ClassDeclaration): void {\n const decorators = ts.getDecorators(classNode);\n if (!decorators) return;\n\n for (const decorator of decorators) {\n if (!ts.isCallExpression(decorator.expression)) continue;\n\n const expr = decorator.expression;\n if (!ts.isIdentifier(expr.expression)) continue;\n if (expr.expression.text !== 'Component') continue;\n\n processComponentDecorator(expr);\n }\n }\n\n function processComponentDecorator(callExpr: ts.CallExpression): void {\n if (callExpr.arguments.length === 0) {\n // Empty decorator call: @Component() — add host arg\n const line = sourceFile.getLineAndCharacterOfPosition(callExpr.getStart()).line;\n const sourceAttr = `'data-ng-source': '${relativePath}:${line}:0'`;\n const insertText = `{ host: { ${sourceAttr} } }`;\n\n const openParen = callExpr.getStart() + callExpr.expression.getText().length;\n // Find the actual position of the opening paren\n const parenPos = code.indexOf('(', openParen);\n const closeParenPos = code.indexOf(')', parenPos);\n\n edits.push({\n pos: parenPos + 1,\n end: closeParenPos,\n text: insertText,\n });\n return;\n }\n\n const arg = callExpr.arguments[0];\n if (!ts.isObjectLiteralExpression(arg)) return;\n\n const line = sourceFile.getLineAndCharacterOfPosition(callExpr.getStart()).line;\n const sourceAttr = `'data-ng-source': '${relativePath}:${line}:0'`;\n\n // Check if host property already exists\n const hostProp = arg.properties.find(\n (p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === 'host',\n ) as ts.PropertyAssignment | undefined;\n\n if (hostProp) {\n // Host property exists — inject into it\n if (!ts.isObjectLiteralExpression(hostProp.initializer)) return;\n\n const hostObj = hostProp.initializer;\n\n // Check for existing data-ng-source (idempotent)\n const hasExisting = hostObj.properties.some(\n (p) =>\n ts.isPropertyAssignment(p) &&\n ((ts.isStringLiteral(p.name) && p.name.text === 'data-ng-source') ||\n (ts.isIdentifier(p.name) && p.name.text === 'data-ng-source')),\n );\n\n if (hasExisting) return;\n\n // Insert into existing host object\n if (hostObj.properties.length > 0) {\n const lastProp = hostObj.properties[hostObj.properties.length - 1];\n edits.push({\n pos: lastProp.getEnd(),\n end: lastProp.getEnd(),\n text: `, ${sourceAttr}`,\n });\n } else {\n // Empty host object: host: {}\n const openBrace = hostObj.getStart();\n const closeBrace = hostObj.getEnd();\n edits.push({\n pos: openBrace + 1,\n end: closeBrace - 1,\n text: ` ${sourceAttr} `,\n });\n }\n } else {\n // No host property — add one\n if (arg.properties.length > 0) {\n const lastProp = arg.properties[arg.properties.length - 1];\n edits.push({\n pos: lastProp.getEnd(),\n end: lastProp.getEnd(),\n text: `, host: { ${sourceAttr} }`,\n });\n } else {\n // Empty object literal: @Component({})\n const openBrace = arg.getStart();\n const closeBrace = arg.getEnd();\n edits.push({\n pos: openBrace + 1,\n end: closeBrace - 1,\n text: ` host: { ${sourceAttr} } `,\n });\n }\n }\n }\n}\n"],"mappings":"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","names":["ts","path","line","sourceAttr"]}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
// src/esbuild-plugin/scan.ts
|
|
2
|
-
import ts from "typescript";
|
|
3
|
-
|
|
4
|
-
// src/esbuild-plugin/transform.ts
|
|
5
|
-
import ts2 from "typescript";
|
|
6
|
-
import path from "path";
|
|
7
|
-
function transformAngularComponent(code, filePath, rootDir) {
|
|
8
|
-
const sourceFile = ts2.createSourceFile(
|
|
9
|
-
filePath,
|
|
10
|
-
code,
|
|
11
|
-
ts2.ScriptTarget.Latest,
|
|
12
|
-
true,
|
|
13
|
-
ts2.ScriptKind.TS
|
|
14
|
-
);
|
|
15
|
-
const relativePath = path.relative(rootDir, filePath).replace(/\\/g, "/").replace(/'/g, "\\'");
|
|
16
|
-
const edits = [];
|
|
17
|
-
visitNode(sourceFile);
|
|
18
|
-
if (edits.length === 0) return null;
|
|
19
|
-
edits.sort((a, b) => b.pos - a.pos);
|
|
20
|
-
let result = code;
|
|
21
|
-
for (const edit of edits) {
|
|
22
|
-
result = result.slice(0, edit.pos) + edit.text + result.slice(edit.end);
|
|
23
|
-
}
|
|
24
|
-
return { code: result };
|
|
25
|
-
function visitNode(node) {
|
|
26
|
-
if (ts2.isClassDeclaration(node)) {
|
|
27
|
-
processClassDeclaration(node);
|
|
28
|
-
}
|
|
29
|
-
ts2.forEachChild(node, visitNode);
|
|
30
|
-
}
|
|
31
|
-
function processClassDeclaration(classNode) {
|
|
32
|
-
const decorators = ts2.getDecorators(classNode);
|
|
33
|
-
if (!decorators) return;
|
|
34
|
-
for (const decorator of decorators) {
|
|
35
|
-
if (!ts2.isCallExpression(decorator.expression)) continue;
|
|
36
|
-
const expr = decorator.expression;
|
|
37
|
-
if (!ts2.isIdentifier(expr.expression)) continue;
|
|
38
|
-
if (expr.expression.text !== "Component") continue;
|
|
39
|
-
processComponentDecorator(expr);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
function processComponentDecorator(callExpr) {
|
|
43
|
-
if (callExpr.arguments.length === 0) {
|
|
44
|
-
const line2 = sourceFile.getLineAndCharacterOfPosition(callExpr.getStart()).line;
|
|
45
|
-
const sourceAttr2 = `'data-ng-source': '${relativePath}:${line2}:0'`;
|
|
46
|
-
const insertText = `{ host: { ${sourceAttr2} } }`;
|
|
47
|
-
const openParen = callExpr.getStart() + callExpr.expression.getText().length;
|
|
48
|
-
const parenPos = code.indexOf("(", openParen);
|
|
49
|
-
const closeParenPos = code.indexOf(")", parenPos);
|
|
50
|
-
edits.push({
|
|
51
|
-
pos: parenPos + 1,
|
|
52
|
-
end: closeParenPos,
|
|
53
|
-
text: insertText
|
|
54
|
-
});
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
const arg = callExpr.arguments[0];
|
|
58
|
-
if (!ts2.isObjectLiteralExpression(arg)) return;
|
|
59
|
-
const line = sourceFile.getLineAndCharacterOfPosition(callExpr.getStart()).line;
|
|
60
|
-
const sourceAttr = `'data-ng-source': '${relativePath}:${line}:0'`;
|
|
61
|
-
const hostProp = arg.properties.find(
|
|
62
|
-
(p) => ts2.isPropertyAssignment(p) && ts2.isIdentifier(p.name) && p.name.text === "host"
|
|
63
|
-
);
|
|
64
|
-
if (hostProp) {
|
|
65
|
-
if (!ts2.isObjectLiteralExpression(hostProp.initializer)) return;
|
|
66
|
-
const hostObj = hostProp.initializer;
|
|
67
|
-
const hasExisting = hostObj.properties.some(
|
|
68
|
-
(p) => ts2.isPropertyAssignment(p) && (ts2.isStringLiteral(p.name) && p.name.text === "data-ng-source" || ts2.isIdentifier(p.name) && p.name.text === "data-ng-source")
|
|
69
|
-
);
|
|
70
|
-
if (hasExisting) return;
|
|
71
|
-
if (hostObj.properties.length > 0) {
|
|
72
|
-
const lastProp = hostObj.properties[hostObj.properties.length - 1];
|
|
73
|
-
edits.push({
|
|
74
|
-
pos: lastProp.getEnd(),
|
|
75
|
-
end: lastProp.getEnd(),
|
|
76
|
-
text: `, ${sourceAttr}`
|
|
77
|
-
});
|
|
78
|
-
} else {
|
|
79
|
-
const openBrace = hostObj.getStart();
|
|
80
|
-
const closeBrace = hostObj.getEnd();
|
|
81
|
-
edits.push({
|
|
82
|
-
pos: openBrace + 1,
|
|
83
|
-
end: closeBrace - 1,
|
|
84
|
-
text: ` ${sourceAttr} `
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
} else {
|
|
88
|
-
if (arg.properties.length > 0) {
|
|
89
|
-
const lastProp = arg.properties[arg.properties.length - 1];
|
|
90
|
-
edits.push({
|
|
91
|
-
pos: lastProp.getEnd(),
|
|
92
|
-
end: lastProp.getEnd(),
|
|
93
|
-
text: `, host: { ${sourceAttr} }`
|
|
94
|
-
});
|
|
95
|
-
} else {
|
|
96
|
-
const openBrace = arg.getStart();
|
|
97
|
-
const closeBrace = arg.getEnd();
|
|
98
|
-
edits.push({
|
|
99
|
-
pos: openBrace + 1,
|
|
100
|
-
end: closeBrace - 1,
|
|
101
|
-
text: ` host: { ${sourceAttr} } `
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// src/vite-plugin/index.ts
|
|
109
|
-
function angularGrabVitePlugin(options) {
|
|
110
|
-
return {
|
|
111
|
-
name: "angular-grab-source-injector",
|
|
112
|
-
enforce: "pre",
|
|
113
|
-
apply: "serve",
|
|
114
|
-
transform(code, id) {
|
|
115
|
-
if (!id.endsWith(".component.ts") || !code.includes("@Component")) {
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
const rootDir = options?.rootDir || process.cwd();
|
|
119
|
-
return transformAngularComponent(code, id, rootDir);
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
var vite_plugin_default = angularGrabVitePlugin;
|
|
124
|
-
export {
|
|
125
|
-
angularGrabVitePlugin,
|
|
126
|
-
vite_plugin_default as default
|
|
127
|
-
};
|
|
128
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/esbuild-plugin/scan.ts","../../src/esbuild-plugin/transform.ts","../../src/vite-plugin/index.ts"],"sourcesContent":["import ts from 'typescript';\nimport path from 'path';\nimport fs from 'fs';\n\nexport interface ComponentSourceInfo {\n file: string;\n line: number;\n}\n\nexport type SourceMap = Record<string, ComponentSourceInfo>;\n\n/**\n * Scans the project for `.component.ts` files and extracts component\n * class names with their source locations. Returns a map of\n * `{ ComponentName: { file, line } }`.\n */\nexport function scanComponentSources(rootDir: string): SourceMap {\n const sourceMap: SourceMap = {};\n const files = findComponentFiles(rootDir);\n\n for (const filePath of files) {\n const content = fs.readFileSync(filePath, 'utf8');\n if (!content.includes('@Component')) continue;\n\n const relativePath = path.relative(rootDir, filePath).replace(/\\\\/g, '/');\n const components = extractComponents(content, filePath);\n\n for (const comp of components) {\n sourceMap[comp.name] = { file: relativePath, line: comp.line };\n }\n }\n\n return sourceMap;\n}\n\ninterface ComponentInfo {\n name: string;\n line: number;\n}\n\nfunction extractComponents(code: string, filePath: string): ComponentInfo[] {\n const sourceFile = ts.createSourceFile(\n filePath,\n code,\n ts.ScriptTarget.Latest,\n true,\n ts.ScriptKind.TS,\n );\n\n const results: ComponentInfo[] = [];\n\n function visit(node: ts.Node): void {\n if (ts.isClassDeclaration(node) && node.name) {\n const decorators = ts.getDecorators(node);\n if (!decorators) return;\n\n const hasComponent = decorators.some((d) => {\n if (!ts.isCallExpression(d.expression)) return false;\n if (!ts.isIdentifier(d.expression.expression)) return false;\n return d.expression.expression.text === 'Component';\n });\n\n if (hasComponent) {\n const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());\n results.push({ name: node.name.text, line: line + 1 });\n }\n }\n\n ts.forEachChild(node, visit);\n }\n\n visit(sourceFile);\n return results;\n}\n\nfunction findComponentFiles(rootDir: string): string[] {\n const results: string[] = [];\n const srcDir = path.join(rootDir, 'src');\n\n if (!fs.existsSync(srcDir)) return results;\n\n walk(srcDir, results);\n return results;\n}\n\nfunction walk(dir: string, results: string[]): void {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue;\n\n const fullPath = path.join(dir, entry.name);\n\n if (entry.isDirectory()) {\n walk(fullPath, results);\n } else if (entry.name.endsWith('.component.ts')) {\n results.push(fullPath);\n }\n }\n}\n","import ts from 'typescript';\nimport path from 'path';\n\nexport interface TransformResult {\n code: string;\n}\n\ninterface Edit {\n pos: number;\n end: number;\n text: string;\n}\n\nexport function transformAngularComponent(\n code: string,\n filePath: string,\n rootDir: string,\n): TransformResult | null {\n const sourceFile = ts.createSourceFile(\n filePath,\n code,\n ts.ScriptTarget.Latest,\n true,\n ts.ScriptKind.TS,\n );\n\n const relativePath = path.relative(rootDir, filePath).replace(/\\\\/g, '/').replace(/'/g, \"\\\\'\");\n const edits: Edit[] = [];\n\n visitNode(sourceFile);\n\n if (edits.length === 0) return null;\n\n // Apply edits in reverse order to preserve positions\n edits.sort((a, b) => b.pos - a.pos);\n\n let result = code;\n for (const edit of edits) {\n result = result.slice(0, edit.pos) + edit.text + result.slice(edit.end);\n }\n\n return { code: result };\n\n function visitNode(node: ts.Node): void {\n if (ts.isClassDeclaration(node)) {\n processClassDeclaration(node);\n }\n ts.forEachChild(node, visitNode);\n }\n\n function processClassDeclaration(classNode: ts.ClassDeclaration): void {\n const decorators = ts.getDecorators(classNode);\n if (!decorators) return;\n\n for (const decorator of decorators) {\n if (!ts.isCallExpression(decorator.expression)) continue;\n\n const expr = decorator.expression;\n if (!ts.isIdentifier(expr.expression)) continue;\n if (expr.expression.text !== 'Component') continue;\n\n processComponentDecorator(expr);\n }\n }\n\n function processComponentDecorator(callExpr: ts.CallExpression): void {\n if (callExpr.arguments.length === 0) {\n // Empty decorator call: @Component() — add host arg\n const line = sourceFile.getLineAndCharacterOfPosition(callExpr.getStart()).line;\n const sourceAttr = `'data-ng-source': '${relativePath}:${line}:0'`;\n const insertText = `{ host: { ${sourceAttr} } }`;\n\n const openParen = callExpr.getStart() + callExpr.expression.getText().length;\n // Find the actual position of the opening paren\n const parenPos = code.indexOf('(', openParen);\n const closeParenPos = code.indexOf(')', parenPos);\n\n edits.push({\n pos: parenPos + 1,\n end: closeParenPos,\n text: insertText,\n });\n return;\n }\n\n const arg = callExpr.arguments[0];\n if (!ts.isObjectLiteralExpression(arg)) return;\n\n const line = sourceFile.getLineAndCharacterOfPosition(callExpr.getStart()).line;\n const sourceAttr = `'data-ng-source': '${relativePath}:${line}:0'`;\n\n // Check if host property already exists\n const hostProp = arg.properties.find(\n (p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === 'host',\n ) as ts.PropertyAssignment | undefined;\n\n if (hostProp) {\n // Host property exists — inject into it\n if (!ts.isObjectLiteralExpression(hostProp.initializer)) return;\n\n const hostObj = hostProp.initializer;\n\n // Check for existing data-ng-source (idempotent)\n const hasExisting = hostObj.properties.some(\n (p) =>\n ts.isPropertyAssignment(p) &&\n ((ts.isStringLiteral(p.name) && p.name.text === 'data-ng-source') ||\n (ts.isIdentifier(p.name) && p.name.text === 'data-ng-source')),\n );\n\n if (hasExisting) return;\n\n // Insert into existing host object\n if (hostObj.properties.length > 0) {\n const lastProp = hostObj.properties[hostObj.properties.length - 1];\n edits.push({\n pos: lastProp.getEnd(),\n end: lastProp.getEnd(),\n text: `, ${sourceAttr}`,\n });\n } else {\n // Empty host object: host: {}\n const openBrace = hostObj.getStart();\n const closeBrace = hostObj.getEnd();\n edits.push({\n pos: openBrace + 1,\n end: closeBrace - 1,\n text: ` ${sourceAttr} `,\n });\n }\n } else {\n // No host property — add one\n if (arg.properties.length > 0) {\n const lastProp = arg.properties[arg.properties.length - 1];\n edits.push({\n pos: lastProp.getEnd(),\n end: lastProp.getEnd(),\n text: `, host: { ${sourceAttr} }`,\n });\n } else {\n // Empty object literal: @Component({})\n const openBrace = arg.getStart();\n const closeBrace = arg.getEnd();\n edits.push({\n pos: openBrace + 1,\n end: closeBrace - 1,\n text: ` host: { ${sourceAttr} } `,\n });\n }\n }\n }\n}\n","import type { Plugin } from 'vite';\nimport { transformAngularComponent } from '../esbuild-plugin';\n\nexport function angularGrabVitePlugin(options?: { rootDir?: string }): Plugin {\n return {\n name: 'angular-grab-source-injector',\n enforce: 'pre',\n apply: 'serve',\n transform(code: string, id: string) {\n if (!id.endsWith('.component.ts') || !code.includes('@Component')) {\n return null;\n }\n const rootDir = options?.rootDir || process.cwd();\n return transformAngularComponent(code, id, rootDir);\n },\n };\n}\n\nexport default angularGrabVitePlugin;\n"],"mappings":"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","names":["ts","line","sourceAttr"]}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/webpack-plugin/index.ts
|
|
21
|
-
var webpack_plugin_exports = {};
|
|
22
|
-
__export(webpack_plugin_exports, {
|
|
23
|
-
AngularGrabWebpackPlugin: () => AngularGrabWebpackPlugin
|
|
24
|
-
});
|
|
25
|
-
module.exports = __toCommonJS(webpack_plugin_exports);
|
|
26
|
-
|
|
27
|
-
// ../../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.8_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js
|
|
28
|
-
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
29
|
-
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
30
|
-
|
|
31
|
-
// src/webpack-plugin/plugin.ts
|
|
32
|
-
var import_node_url = require("url");
|
|
33
|
-
var import_node_path = require("path");
|
|
34
|
-
var AngularGrabWebpackPlugin = class {
|
|
35
|
-
apply(compiler) {
|
|
36
|
-
const pluginDir = (0, import_node_path.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
37
|
-
compiler.options.module = compiler.options.module || {};
|
|
38
|
-
compiler.options.module.rules = compiler.options.module.rules || [];
|
|
39
|
-
compiler.options.module.rules.push({
|
|
40
|
-
test: /\.component\.ts$/,
|
|
41
|
-
enforce: "pre",
|
|
42
|
-
use: [
|
|
43
|
-
{
|
|
44
|
-
loader: (0, import_node_path.resolve)(pluginDir, "loader.js")
|
|
45
|
-
}
|
|
46
|
-
]
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
51
|
-
0 && (module.exports = {
|
|
52
|
-
AngularGrabWebpackPlugin
|
|
53
|
-
});
|
|
54
|
-
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/webpack-plugin/index.ts","../../../../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.8_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","../../src/webpack-plugin/plugin.ts"],"sourcesContent":["// Placeholder\nexport { AngularGrabWebpackPlugin } from './plugin';\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","import { fileURLToPath } from 'node:url';\nimport { dirname, resolve } from 'node:path';\n\nexport class AngularGrabWebpackPlugin {\n apply(compiler: any) {\n const pluginDir = dirname(fileURLToPath(import.meta.url));\n\n compiler.options.module = compiler.options.module || {};\n compiler.options.module.rules = compiler.options.module.rules || [];\n compiler.options.module.rules.push({\n test: /\\.component\\.ts$/,\n enforce: 'pre' as const,\n use: [\n {\n loader: resolve(pluginDir, 'loader.js'),\n },\n ],\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,IAAM,mBAAmB,MACvB,OAAO,aAAa,cAChB,IAAI,IAAI,QAAQ,UAAU,EAAE,EAAE,OAC7B,SAAS,iBAAiB,SAAS,cAAc,QAAQ,YAAY,MAAM,WAC1E,SAAS,cAAc,MACvB,IAAI,IAAI,WAAW,SAAS,OAAO,EAAE;AAEtC,IAAM,gBAAgC,iCAAiB;;;ACZ9D,sBAA8B;AAC9B,uBAAiC;AAE1B,IAAM,2BAAN,MAA+B;AAAA,EACpC,MAAM,UAAe;AACnB,UAAM,gBAAY,8BAAQ,+BAAc,aAAe,CAAC;AAExD,aAAS,QAAQ,SAAS,SAAS,QAAQ,UAAU,CAAC;AACtD,aAAS,QAAQ,OAAO,QAAQ,SAAS,QAAQ,OAAO,SAAS,CAAC;AAClE,aAAS,QAAQ,OAAO,MAAM,KAAK;AAAA,MACjC,MAAM;AAAA,MACN,SAAS;AAAA,MACT,KAAK;AAAA,QACH;AAAA,UACE,YAAQ,0BAAQ,WAAW,WAAW;AAAA,QACxC;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;","names":[]}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
// src/webpack-plugin/plugin.ts
|
|
2
|
-
import { fileURLToPath } from "url";
|
|
3
|
-
import { dirname, resolve } from "path";
|
|
4
|
-
var AngularGrabWebpackPlugin = class {
|
|
5
|
-
apply(compiler) {
|
|
6
|
-
const pluginDir = dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
compiler.options.module = compiler.options.module || {};
|
|
8
|
-
compiler.options.module.rules = compiler.options.module.rules || [];
|
|
9
|
-
compiler.options.module.rules.push({
|
|
10
|
-
test: /\.component\.ts$/,
|
|
11
|
-
enforce: "pre",
|
|
12
|
-
use: [
|
|
13
|
-
{
|
|
14
|
-
loader: resolve(pluginDir, "loader.js")
|
|
15
|
-
}
|
|
16
|
-
]
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
export {
|
|
21
|
-
AngularGrabWebpackPlugin
|
|
22
|
-
};
|
|
23
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/webpack-plugin/plugin.ts"],"sourcesContent":["import { fileURLToPath } from 'node:url';\nimport { dirname, resolve } from 'node:path';\n\nexport class AngularGrabWebpackPlugin {\n apply(compiler: any) {\n const pluginDir = dirname(fileURLToPath(import.meta.url));\n\n compiler.options.module = compiler.options.module || {};\n compiler.options.module.rules = compiler.options.module.rules || [];\n compiler.options.module.rules.push({\n test: /\\.component\\.ts$/,\n enforce: 'pre' as const,\n use: [\n {\n loader: resolve(pluginDir, 'loader.js'),\n },\n ],\n });\n }\n}\n"],"mappings":";AAAA,SAAS,qBAAqB;AAC9B,SAAS,SAAS,eAAe;AAE1B,IAAM,2BAAN,MAA+B;AAAA,EACpC,MAAM,UAAe;AACnB,UAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAExD,aAAS,QAAQ,SAAS,SAAS,QAAQ,UAAU,CAAC;AACtD,aAAS,QAAQ,OAAO,QAAQ,SAAS,QAAQ,OAAO,SAAS,CAAC;AAClE,aAAS,QAAQ,OAAO,MAAM,KAAK;AAAA,MACjC,MAAM;AAAA,MACN,SAAS;AAAA,MACT,KAAK;AAAA,QACH;AAAA,UACE,QAAQ,QAAQ,WAAW,WAAW;AAAA,QACxC;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;","names":[]}
|