Skip to content

Commit ae1f3d3

Browse files
intial commit
0 parents  commit ae1f3d3

File tree

12 files changed

+380
-0
lines changed

12 files changed

+380
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.js
2+
*.map
3+
node_modules
4+
package-lock.json

README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Builder To Run Source Map Explorer
2+
3+
Want to run the source-map-explorer with Angular?
4+
This builder does it for you with zero configuration.
5+
6+
7+
## Setting up this Builder
8+
9+
```
10+
ng add @ngx-builders/analyze
11+
```
12+
13+
## That's it. Now, you are good to go
14+
15+
Now whenever you want to deploy your angular project just run a command `ng run [YOUR_PROJECT_NAME]:analyze` and it will run the source-map-explorer.
16+
17+
18+
## 📦 Options <a name="options"></a>
19+
20+
#### --configuration <a name="configuration"></a>
21+
22+
#### --no-build <a name="no-build"></a>
23+
- **optional**
24+
- Default: `false` (string)
25+
- Example:
26+
- `ng run [YOUR_PROJECT_NAME]:analyze` – Angular project is build in production mode before the deployment
27+
- `ng deploy [YOUR_PROJECT_NAME]:analyze --no-build` – Angular project is NOT build
28+
29+
Skip build process during deployment.
30+
This can be used when you are sure that you haven't changed anything and want to deploy with the latest artifact.
31+
This command causes the `--configuration` setting to have no effect.
32+
33+
MIT

analyze/index.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { BuilderOutput, createBuilder, BuilderContext } from '@angular-devkit/architect';
2+
import { json } from '@angular-devkit/core';
3+
import { Schema } from './schema';
4+
import util from 'util';
5+
import { exec } from 'child_process';
6+
import fs from 'fs';
7+
8+
export const execAsync = util.promisify(exec);
9+
10+
export default createBuilder<any>(
11+
async (builderConfig: Schema, context: BuilderContext): Promise<BuilderOutput> => {
12+
try {
13+
context.reportStatus(`Executing "${builderConfig.noBuild}"...`);
14+
// const child = childProcess.spawn(options.command, options.args, { stdio: 'pipe' });
15+
16+
const configuration = 'production';
17+
18+
const overrides = {
19+
// this is an example how to override the workspace set of options
20+
...({ sourceMap: true })
21+
};
22+
23+
24+
const build = await context.scheduleTarget({
25+
target: 'build',
26+
project: context?.target?.project || '',
27+
configuration
28+
}, overrides as json.JsonObject);
29+
30+
const result = await build.result;
31+
const buildPath = 'dist/analyze';
32+
let fileResult: any;
33+
34+
if (result.success) {
35+
const file = fs.readdirSync(builderConfig.outputPath).filter(f => f.includes('main-es2015'));
36+
const mainFile = file.find(f => f.endsWith('.js'));
37+
console.log(mainFile);
38+
39+
const commandToPublish = `source-map-explorer ${builderConfig.outputPath}/${mainFile}`;
40+
41+
const { stdout, stderr } = await execAsync(commandToPublish);
42+
context.logger.info(stdout);
43+
context.logger.info(stderr);
44+
}
45+
46+
context.reportStatus(`Done.`);
47+
return {
48+
success: true
49+
};
50+
}
51+
catch (e) {
52+
return {
53+
error: e.message,
54+
success: true
55+
};
56+
}
57+
});
58+

analyze/index_spec.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { Architect } from '@angular-devkit/architect';
2+
import { TestingArchitectHost } from '@angular-devkit/architect/testing';
3+
import { logging, schema } from '@angular-devkit/core';
4+
5+
const { join } = require('path');
6+
7+
describe('Command Runner Builder', () => {
8+
let architect: Architect;
9+
let architectHost: TestingArchitectHost;
10+
11+
12+
beforeEach(async () => {
13+
const registry = new schema.CoreSchemaRegistry();
14+
registry.addPostTransform(schema.transforms.addUndefinedDefaults);
15+
16+
17+
// Arguments to TestingArchitectHost are workspace and current directories.
18+
// Since we don't use those, both are the same in this case.
19+
architectHost = new TestingArchitectHost(__dirname, __dirname);
20+
architect = new Architect(architectHost, registry);
21+
22+
23+
// This will either take a Node package name, or a path to the directory
24+
// for the package.json file.
25+
await architectHost.addBuilderFromPackage(join(__dirname, '..'));
26+
console.log('#', Array.from((architectHost as any)._builderMap.keys()))
27+
});
28+
29+
30+
// This might not work in Windows.
31+
it('can run ls', async () => {
32+
// Create a logger that keeps an array of all messages that were logged.
33+
const logger = new logging.Logger('');
34+
const logs: string[] = [];
35+
logger.subscribe(ev => logs.push(ev.message));
36+
37+
// A "run" can contain multiple outputs, and contains progress information.
38+
const run = await architect.scheduleBuilder('@example/command-runner:command', {
39+
command: 'ls',
40+
args: [__dirname],
41+
}, { logger }); // We pass the logger for checking later.
42+
43+
44+
// The "result" member is the next output of the runner.
45+
// This is of type BuilderOutput.
46+
const output = await run.result;
47+
48+
// Stop the builder from running. This really stops Architect from keeping
49+
// the builder associated states in memory, since builders keep waiting
50+
// to be scheduled.
51+
await run.stop();
52+
53+
// Expect that it succeeded.
54+
expect(output.success).toBe(true);
55+
56+
// Expect that this file was listed. It should be since we're running
57+
// `ls $__dirname`.
58+
expect(logs.toString()).toContain('index_spec.ts');
59+
});
60+
});

analyze/schema.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface Schema {
2+
noBuild?: boolean;
3+
outputPath: string;
4+
}

analyze/schema.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"$schema": "http://json-schema.org/schema",
3+
"type": "object",
4+
"properties": {
5+
"noBuild": {
6+
"type": "boolean"
7+
},
8+
"outputPath": {
9+
"type": "string"
10+
}
11+
}
12+
}

builders.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"builders": {
3+
"analyze": {
4+
"implementation": "./analyze",
5+
"schema": "./analyze/schema.json",
6+
"description": "Runs the analyzer."
7+
}
8+
}
9+
}

collection.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"$schema": "@angular-devkit/schematics/collection-schema.json",
3+
"schematics": {
4+
"ng-add": {
5+
"description": "Builder to analyze package using source-map-explorer",
6+
"factory": "./ng-add/index",
7+
"schema": "./ng-add/schema.json",
8+
"aliases": [
9+
"install"
10+
]
11+
}
12+
}
13+
}

ng-add/index.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { Rule, SchematicContext, SchematicsException, Tree, chain } from '@angular-devkit/schematics';
2+
import { experimental, JsonParseMode, parseJson } from '@angular-devkit/core';
3+
import { addPackageJsonDependency, NodeDependency, NodeDependencyType } from 'schematics-utilities';
4+
5+
function addPackageJsonDependencies(): Rule {
6+
return (host: Tree, context: SchematicContext) => {
7+
8+
// always add the package under dev dependencies
9+
const dependencies: NodeDependency[] = [
10+
{ type: NodeDependencyType.Dev, version: '~0.0.0', name: '@ngx-builders/analyze' },
11+
{ type: NodeDependencyType.Dev, version: '~2.4.2', name: 'source-map-explorer' }
12+
];
13+
14+
dependencies.forEach(dependency => {
15+
addPackageJsonDependency(host, dependency);
16+
context.logger.log('info', `✅️ Added "${dependency.name}" into ${dependency.type}`);
17+
});
18+
19+
return host;
20+
};
21+
}
22+
23+
function getWorkspace(host: Tree): { path: string; workspace: experimental.workspace.WorkspaceSchema } {
24+
const possibleFiles = ['/angular.json', './angular.json'];
25+
const path = possibleFiles.find(path => host.exists(path));
26+
27+
if (!path) {
28+
throw new SchematicsException(`Could not find angular.json`);
29+
}
30+
31+
const configBuffer = host.read(path);
32+
if (!configBuffer) {
33+
throw new SchematicsException(`Could not find angular.json`);
34+
}
35+
36+
const content = configBuffer.toString();
37+
let workspace: experimental.workspace.WorkspaceSchema;
38+
39+
try {
40+
workspace = <any>parseJson(content, JsonParseMode.Loose) as experimental.workspace.WorkspaceSchema;
41+
} catch (e) {
42+
throw new SchematicsException(`Could not parse angular.json: ${e.message}`);
43+
}
44+
45+
return { path, workspace };
46+
}
47+
48+
interface NgAddOptions {
49+
project?: string;
50+
siteID: string;
51+
netlifyToken: string;
52+
}
53+
54+
export function sourceMapBuilder(options: NgAddOptions): Rule {
55+
return (tree: Tree, _context: SchematicContext) => {
56+
// get the workspace details
57+
const { path: workspacePath, workspace } = getWorkspace(tree);
58+
59+
// getting project name
60+
if (!options.project) {
61+
if (workspace.defaultProject) {
62+
options.project = workspace.defaultProject;
63+
} else {
64+
throw new SchematicsException(
65+
'No Angular project selected and no default project in the workspace'
66+
);
67+
}
68+
}
69+
70+
// Validating project name
71+
const project = workspace.projects[options.project];
72+
if (!project) {
73+
throw new SchematicsException(
74+
'The specified Angular project is not defined in this workspace'
75+
);
76+
}
77+
78+
// Checking if it is application
79+
if (project.projectType !== 'application') {
80+
throw new SchematicsException(
81+
`Deploy requires an Angular project type of "application" in angular.json`
82+
);
83+
}
84+
85+
// Getting output path from Angular.json
86+
if (
87+
!project.architect ||
88+
!project.architect.build ||
89+
!project.architect.build.options ||
90+
!project.architect.build.options.outputPath
91+
) {
92+
throw new SchematicsException(
93+
`Cannot read the output path(architect.build.options.outputPath) of the Angular project "${options.project}" in angular.json`
94+
);
95+
}
96+
97+
// adding deploy statement for builder
98+
project.architect['analyze'] = {
99+
"builder": "@ngx-builders/analyze:analyze",
100+
"options": {
101+
"outputPath": project.architect.build.options.outputPath
102+
}
103+
}
104+
105+
tree.overwrite(workspacePath, JSON.stringify(workspace, null, 2));
106+
return tree;
107+
};
108+
}
109+
110+
export default function (options: NgAddOptions): Rule {
111+
return chain([
112+
sourceMapBuilder(options),
113+
addPackageJsonDependencies()
114+
]);
115+
}

ng-add/schema.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"$schema": "http://json-schema.org/schema",
3+
"id": "source-map-explore",
4+
"title": "Builder to analyze using source-map-explorer",
5+
"type": "object",
6+
"properties": {},
7+
"required": []
8+
}

0 commit comments

Comments
 (0)