Skip to content
This repository has been archived by the owner on Mar 4, 2023. It is now read-only.

Commit

Permalink
Register assets in angular.json (#10)
Browse files Browse the repository at this point in the history
* register build and test assets

* fix default parser for prettier

* configuration interface fixes

* logger implementation

* register assets only once

* code cleanup
  • Loading branch information
DenysVuika authored Oct 11, 2018
1 parent 21e3eee commit 6ed53d2
Show file tree
Hide file tree
Showing 7 changed files with 222 additions and 39 deletions.
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ Example:
{
"assets": [
{
"from": "assets/my-extension.json",
"to": "plugins"
"glob": "**/*.json",
"input": "./assets",
"output": "./assets/plugins"
}
],
"modules": [
Expand All @@ -61,10 +62,23 @@ Example:

Based on the configuration above, the tool is going to perform the following actions:

- copy a file `assets/my-extension` to the `src/assets/plugin` folder
- copy all JSON files from `assets` to the `assets/plugin` folder
- generate `import { MyExtensionModule } from 'my-extension';` in the `/src/app/app.module.ts`
- put the `MyExtensionModule` into the module `imports` section

### Asset configuration

The asset configuration format is based on the [Angular CLI settings](https://github.com/angular/angular-cli/wiki/stories-asset-configuration).
However, the `input` property defines the path relative to the library structure, rather than application one.

```json
{
"glob": "**/*.json",
"input": "./assets",
"output": "./assets/plugins"
}
```

## Examples

Install library `my-extension` from the NPM
Expand Down
23 changes: 23 additions & 0 deletions src/angular-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// partial, contains only the info needed by the tool

interface AngularConfig {
defaultProject: string;
projects: {
[key: string]: {
architect: {
build: {
options: {
assets: Array<any>;
};
};
test: {
options: {
assets: Array<any>;
};
};
};
};
};
}

export default AngularConfig;
142 changes: 142 additions & 0 deletions src/assets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import path from 'path';
import fs from 'fs';

import log from './log';
import AngularConfig from './angular-config';
import { Configuration, GlobRule } from './configuration';

export function registerAssets(lib: string, config: Configuration) {
if (!config || !config.assets || config.assets.length === 0) {
return;
}

log.info('registering assets');

const configPath = path.join(process.cwd(), 'angular.json');
let angularConfig: AngularConfig;

try {
angularConfig = require(configPath);
} catch {
log.warning('angular.json file not found.');
return;
}

const project = angularConfig.projects[angularConfig.defaultProject];
if (!project) {
log.warning(`project ${angularConfig.defaultProject} not found`);
return;
}

const buildAssets = project.architect.build.options.assets || [];
const testAssets = project.architect.test.options.assets || [];

for (let rule of config.assets) {
if (typeof rule === 'object') {
registerObject(lib, buildAssets, rule);
registerObject(lib, testAssets, rule);
}

if (typeof rule === 'string') {
registerString(lib, buildAssets, rule);
registerString(lib, testAssets, rule);
}
}

project.architect.build.options.assets = buildAssets;
project.architect.test.options.assets = testAssets;

fs.writeFileSync(configPath, JSON.stringify(angularConfig, null, 2));
}

function registerObject(
lib: string,
assets: Array<string | GlobRule>,
rule: GlobRule
) {
if (!isValidObjectRule(rule)) {
log.warning(`skipping invalid object rule`);
return;
}

const input = path.relative(
process.cwd(),
path.join(process.cwd(), 'node_modules', lib, rule.input)
);

const finalRule = { ...rule, input };

if (!isRegisteredObject(assets, finalRule)) {
assets.push(finalRule);
}
}

function registerString(
lib: string,
assets: Array<string | GlobRule>,
rule: string
) {
if (rule && assets) {
const input = path.relative(
process.cwd(),
path.join(process.cwd(), 'node_modules', lib, rule)
);

if (!isRegisteredString(assets, input)) {
assets.push(input);
}
}
}

function isValidObjectRule(rule: GlobRule): boolean {
if (rule && rule.glob && rule.input && rule.output) {
return true;
}
return false;
}

function isRegisteredString(
assets: Array<string | GlobRule>,
rule: string
): boolean {
const stringRules = assets.filter(entry => typeof entry === 'string');
return stringRules.includes(rule);
}

function isRegisteredObject(
assets: Array<string | GlobRule>,
rule: GlobRule
): boolean {
const objectRules = assets
.filter(entry => typeof entry === 'object')
.map(entry => entry as GlobRule);

return objectRules.some(existing => {
return (
existing.glob === rule.glob &&
existing.input === rule.input &&
existing.output === rule.output
);
});
}

/*
export function copyAssets(lib: string, config: Configuration): void {
if (config && config.assets && config.assets.length > 0) {
const libPath = path.join(process.cwd(), 'node_modules', lib);
const localPath = path.join(process.cwd(), 'src/assets');
sh.mkdir('-p', localPath);
config.assets.forEach(asset => {
const from = path.join(libPath, asset.from);
const to = path.join(localPath, asset.to || '');
sh.mkdir('-p', to);
console.log(chalk.blue('info'), `copy ${asset.from}`);
sh.cp(from, to);
});
}
}
*/
13 changes: 7 additions & 6 deletions src/configuration.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
interface Configuration {
assets?: Array<{
from: string;
to?: string;
}>;
export interface Configuration {
assets?: Array<string | GlobRule>;

modules?: Array<{
name: string;
namespace: string;
}>;
}

export default Configuration;
export interface GlobRule {
glob: string;
input: string;
output: string;
}
20 changes: 20 additions & 0 deletions src/log.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import chalk from 'chalk';

class Log {
success(message: string): void {
console.log(chalk.green('success'), message);
}
warning(message: string): void {
console.log(chalk.yellow('warning'), message);
}

info(message: string): void {
console.log(chalk.blue('info'), message);
}

error(message: string): void {
console.log(chalk.red('error'), message);
}
}

export default new Log();
10 changes: 4 additions & 6 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import program from 'commander';
import {
loadConfig,
install,
copyAssets,
registerModules,
createConfig,
version
} from './utils';
import Options from './options';
import chalk from 'chalk';
import log from './log';
import { registerAssets } from './assets';

program
.version(version(), '-v, --version')
Expand All @@ -28,7 +28,7 @@ program
.action((lib: string, name: string, options: Options) => {
if (options.init) {
createConfig();
console.log(chalk.green('success'), 'Created new ngi.json file');
log.success('Created new ngi.json file');
console.log('✨ Done');
return;
}
Expand All @@ -47,11 +47,9 @@ program
const config = loadConfig(libName);
if (config) {
if (!options.skipAssets) {
console.log(chalk.blue('info'), 'copying assets');
copyAssets(libName, config);
registerAssets(libName, config);
}
if (!options.skipModule) {
console.log(chalk.blue('info'), 'registering modules');
registerModules(moduleName, config, options.skipFormat);
}
}
Expand Down
33 changes: 9 additions & 24 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import sh from 'shelljs';
import path from 'path';
import fs from 'fs';
import chalk from 'chalk';
import prettier from 'prettier';

import Configuration from './configuration';
import TsUtils from './ts-utils';
import log from './log';
import { Configuration } from './configuration';

export function loadConfig(lib: string): Configuration | undefined {
const configPath = path.join(process.cwd(), 'node_modules', lib, 'ngi.json');

try {
return require(configPath);
} catch {
console.log(chalk.yellow('warning'), 'Configuration file not found.');
log.warning('Configuration file not found.');
return undefined;
}
}
Expand All @@ -37,7 +37,7 @@ export function install(lib: string): boolean {
return false;
}

console.log(chalk.blue('info'), `Installing ${lib}`);
log.info(`Installing ${lib}`);

if (sh.exec(`npm i ${lib}`).code !== 0) {
return false;
Expand All @@ -46,31 +46,14 @@ export function install(lib: string): boolean {
return true;
}

export function copyAssets(lib: string, config: Configuration): void {
if (config && config.assets && config.assets.length > 0) {
const libPath = path.join(process.cwd(), 'node_modules', lib);
const localPath = path.join(process.cwd(), 'src/assets');

sh.mkdir('-p', localPath);

config.assets.forEach(asset => {
const from = path.join(libPath, asset.from);
const to = path.join(localPath, asset.to || '');

sh.mkdir('-p', to);

console.log(chalk.blue('info'), `copy ${asset.from}`);
sh.cp(from, to);
});
}
}

export function registerModules(
moduleName: string,
config: Configuration,
skipFormat?: boolean
): void {
if (config && config.modules && config.modules.length > 0) {
log.info('registering modules');

const modulePath = path.join(
process.cwd(),
'src/app',
Expand All @@ -94,12 +77,14 @@ export function version(): string {
}

export function format(source: string): string {
console.log(chalk.blue('info'), `formatting code`);
log.info('formatting code');

const options = prettier.resolveConfig.sync(process.cwd()) || {
parser: 'typescript',
singleQuote: true
};

options.parser = options.parser || 'typescript';

return prettier.format(source, options);
}

0 comments on commit 6ed53d2

Please sign in to comment.