Skip to content

Commit

Permalink
feat(react): add assets option for package builder to copy files to o…
Browse files Browse the repository at this point in the history
…utput path (#2933)
  • Loading branch information
jaysoo committed May 1, 2020
1 parent c4e3312 commit 8e6ad2f
Show file tree
Hide file tree
Showing 12 changed files with 184 additions and 52 deletions.
6 changes: 6 additions & 0 deletions docs/angular/api-web/builders/package.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ Builder properties can be configured in angular.json when defining the builder,

## Properties

### assets

Type: `array`

List of static assets.

### babelConfig

Type: `string`
Expand Down
6 changes: 6 additions & 0 deletions docs/react/api-web/builders/package.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ Read more about how to use builders and the CLI here: https://nx.dev/react/guide

## Properties

### assets

Type: `array`

List of static assets.

### babelConfig

Type: `string`
Expand Down
12 changes: 12 additions & 0 deletions e2e/react-package.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
checkFilesDoNotExist,
checkFilesExist,
ensureProject,
forEachCli,
readJson,
Expand Down Expand Up @@ -71,6 +72,16 @@ forEachCli('nx', (cli) => {
json.compilerOptions.paths = {};
return JSON.stringify(json, null, 2);
});

// Add assets to child lib
updateFile(cli === 'angular' ? 'angular.json' : 'workspace.json', (c) => {
const json = JSON.parse(c);
json.projects[childLib].architect.build.options.assets = [
`libs/${childLib}/src/assets`,
];
return JSON.stringify(json, null, 2);
});
updateFile(`libs/${childLib}/src/assets/hello.txt`, 'Hello World!');
});

it('should throw an error if the dependent library has not been built before building the parent lib', () => {
Expand All @@ -90,6 +101,7 @@ forEachCli('nx', (cli) => {
const output = runCLI(`build ${childLib}`);
expect(output).toContain(`${childLib}.esm.js`);
expect(output).toContain(`Bundle complete`);
checkFilesExist(`dist/libs/${childLib}/assets/hello.txt`);
});

it('should properly add references to any dependency into the parent package.json', () => {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@
"release-it": "^7.4.0",
"rollup": "1.31.1",
"rollup-plugin-babel": "4.3.3",
"rollup-plugin-copy": "3.3.0",
"rollup-plugin-filesize": "6.2.1",
"rollup-plugin-local-resolve": "1.0.7",
"rollup-plugin-peer-deps-external": "2.2.2",
Expand Down
1 change: 1 addition & 0 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"regenerator-runtime": "0.13.3",
"rollup": "1.31.1",
"rollup-plugin-babel": "4.3.3",
"rollup-plugin-copy": "3.3.0",
"rollup-plugin-filesize": "6.2.1",
"rollup-plugin-local-resolve": "1.0.7",
"rollup-plugin-peer-deps-external": "2.2.2",
Expand Down
9 changes: 3 additions & 6 deletions packages/web/src/builders/package/package.impl.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,15 @@ import { MockBuilderContext } from '@nrwl/workspace/testing';
import * as impl from './package.impl';
import * as rr from './run-rollup';
import { getMockContext } from '../../utils/testing';
import { BundleBuilderOptions } from '../../utils/types';
import { PackageBuilderOptions } from '../../utils/types';
import * as projectGraphUtils from '@nrwl/workspace/src/core/project-graph';
import {
ProjectGraph,
ProjectType,
} from '@nrwl/workspace/src/core/project-graph';
import { ProjectGraph } from '@nrwl/workspace/src/core/project-graph';

jest.mock('tsconfig-paths-webpack-plugin');

describe('WebPackagebuilder', () => {
let context: MockBuilderContext;
let testOptions: BundleBuilderOptions;
let testOptions: PackageBuilderOptions;
let runRollup: jasmine.Spy;
let writeJsonFile: jasmine.Spy;

Expand Down
60 changes: 48 additions & 12 deletions packages/web/src/builders/package/package.impl.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { relative } from 'path';
import { join, relative } from 'path';
import {
BuilderContext,
BuilderOutput,
createBuilder,
} from '@angular-devkit/architect';
import { JsonObject } from '@angular-devkit/core';
import { Observable, of } from 'rxjs';
import { from, Observable, of } from 'rxjs';
import { catchError, last, switchMap, tap } from 'rxjs/operators';
import { runRollup } from './run-rollup';
import { createBabelConfig as _createBabelConfig } from '../../utils/babel-config';
Expand All @@ -16,10 +16,11 @@ import * as peerDepsExternal from 'rollup-plugin-peer-deps-external';
import * as postcss from 'rollup-plugin-postcss';
import * as filesize from 'rollup-plugin-filesize';
import * as localResolve from 'rollup-plugin-local-resolve';
import { BundleBuilderOptions } from '../../utils/types';
import { PackageBuilderOptions } from '../../utils/types';
import {
normalizeBundleOptions,
normalizePackageOptions,
NormalizedBundleBuilderOptions,
NormalizedCopyAssetOption,
} from '../../utils/normalize';
import { toClassName } from '@nrwl/workspace/src/utils/name-utils';
import { BuildResult } from '@angular-devkit/build-webpack';
Expand All @@ -31,18 +32,21 @@ import { createProjectGraph } from '@nrwl/workspace/src/core/project-graph';
import {
calculateProjectDependencies,
checkDependentProjectsHaveBeenBuilt,
DependentBuildableProjectNode,
computeCompilerOptionsPaths,
DependentBuildableProjectNode,
updateBuildableProjectPackageJsonDependencies,
} from '@nrwl/workspace/src/utils/buildable-libs-utils';
import { getSourceRoot } from '../../utils/source-root';
import { NodeJsSyncHost } from '@angular-devkit/core/node';

// These use require because the ES import isn't correct.
const resolve = require('@rollup/plugin-node-resolve');
const commonjs = require('@rollup/plugin-commonjs');
const typescript = require('rollup-plugin-typescript2');
const image = require('@rollup/plugin-image');
const copy = require('rollup-plugin-copy');

export default createBuilder<BundleBuilderOptions & JsonObject>(run);
export default createBuilder<PackageBuilderOptions & JsonObject>(run);

interface OutputConfig {
format: rollup.ModuleFormat;
Expand All @@ -58,21 +62,27 @@ const outputConfigs: OutputConfig[] = [
const fileExtensions = ['.js', '.jsx', '.ts', '.tsx'];

export function run(
_options: BundleBuilderOptions,
rawOptions: PackageBuilderOptions,
context: BuilderContext
): Observable<BuilderOutput> {
const host = new NodeJsSyncHost();
const projGraph = createProjectGraph();
const { target, dependencies } = calculateProjectDependencies(
projGraph,
context
);

return of(checkDependentProjectsHaveBeenBuilt(context, dependencies)).pipe(
switchMap((result) => {
if (!result) {
return from(getSourceRoot(context, host)).pipe(
switchMap((sourceRoot) => {
if (!checkDependentProjectsHaveBeenBuilt(context, dependencies)) {
return of({ success: false });
}
const options = normalizeBundleOptions(_options, context.workspaceRoot);

const options = normalizePackageOptions(
rawOptions,
context.workspaceRoot,
sourceRoot
);
const packageJson = readJsonFile(options.project);
const rollupOptions = createRollupOptions(
options,
Expand Down Expand Up @@ -153,6 +163,12 @@ function createRollupOptions(
);

const plugins = [
copy({
targets: convertCopyAssetsToRollupOptions(
options.outputPath,
options.assets
),
}),
image(),
typescript({
check: true,
Expand Down Expand Up @@ -221,7 +237,10 @@ function createRollupOptions(
: rollupConfig;
}

function createBabelConfig(options: BundleBuilderOptions, projectRoot: string) {
function createBabelConfig(
options: PackageBuilderOptions,
projectRoot: string
) {
let babelConfig: any = _createBabelConfig(projectRoot, false, false);
if (options.babelConfig) {
babelConfig = require(options.babelConfig)(babelConfig, options);
Expand Down Expand Up @@ -285,3 +304,20 @@ function updatePackageJson(
);
}
}

interface RollupCopyAssetOption {
src: string;
dest: string;
}

function convertCopyAssetsToRollupOptions(
outputPath: string,
assets: NormalizedCopyAssetOption[]
): RollupCopyAssetOption[] {
return assets
? assets.map((a) => ({
src: join(a.input, a.glob),
dest: join(outputPath, a.output),
}))
: undefined;
}
41 changes: 39 additions & 2 deletions packages/web/src/builders/package/schema.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"title": "Web Library Package Target (Experimental)",
"description": "Pckages a library for web different web usages (UMD, ESM, CJS).",
"description": "Packages a library for web different web usages (UMD, ESM, CJS).",
"type": "object",
"properties": {
"project": {
Expand Down Expand Up @@ -67,7 +67,44 @@
"type": "boolean",
"description": "CSS files will be extracted to the output folder.",
"default": true
},
"assets": {
"type": "array",
"description": "List of static assets.",
"default": [],
"items": {
"$ref": "#/definitions/assetPattern"
}
}
},
"required": ["tsConfig", "project", "entryFile", "outputPath"]
"required": ["tsConfig", "project", "entryFile", "outputPath"],

"definitions": {
"assetPattern": {
"oneOf": [
{
"type": "object",
"properties": {
"glob": {
"type": "string",
"description": "The pattern to match."
},
"input": {
"type": "string",
"description": "The input directory path in which to apply 'glob'. Defaults to the project root."
},
"output": {
"type": "string",
"description": "Relative path within the output folder."
}
},
"additionalProperties": false,
"required": ["glob", "input", "output"]
},
{
"type": "string"
}
]
}
}
}
19 changes: 10 additions & 9 deletions packages/web/src/utils/normalize.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { normalizeBuildOptions, normalizeBundleOptions } from './normalize';
import { BuildBuilderOptions, BundleBuilderOptions } from './types';
import { normalizeBuildOptions, normalizePackageOptions } from './normalize';
import { BuildBuilderOptions, PackageBuilderOptions } from './types';
import { Path, normalize } from '@angular-devkit/core';

import * as fs from 'fs';
Expand Down Expand Up @@ -114,8 +114,8 @@ describe('normalizeBuildOptions', () => {
});
});

describe('normalizeBundleOptions', () => {
let testOptions: BundleBuilderOptions;
describe('normalizePackageOptions', () => {
let testOptions: PackageBuilderOptions;
let root: string;
let sourceRoot: Path;

Expand All @@ -133,30 +133,31 @@ describe('normalizeBundleOptions', () => {
});

it('should resolve both node modules and relative path for babelConfig/rollupConfig', () => {
let result = normalizeBundleOptions(testOptions, root);
let result = normalizePackageOptions(testOptions, root, sourceRoot);
expect(result.babelConfig).toEqual('/root/apps/nodeapp/babel.config');
expect(result.rollupConfig).toEqual('/root/apps/nodeapp/rollup.config');

result = normalizeBundleOptions(
result = normalizePackageOptions(
{
...testOptions,
// something that exists in node_modules
rollupConfig: 'react',
babelConfig: 'react',
},
root
root,
sourceRoot
);
expect(result.babelConfig).toMatch('react');
expect(result.babelConfig).not.toMatch(root);
expect(result.rollupConfig).toMatch('react');
expect(result.rollupConfig).not.toMatch(root);
});

it('should handle babelConfig/rollupCofig being undefined', () => {
it('should handle babelConfig/rollupConfig being undefined', () => {
delete testOptions.babelConfig;
delete testOptions.rollupConfig;

const result = normalizeBundleOptions(testOptions, root);
const result = normalizePackageOptions(testOptions, root, sourceRoot);

expect(result.babelConfig).toEqual('');
expect(result.rollupConfig).toEqual('');
Expand Down
27 changes: 19 additions & 8 deletions packages/web/src/utils/normalize.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import { WebBuildBuilderOptions } from '../builders/build/build.impl';
import { normalize } from '@angular-devkit/core';
import { resolve, dirname, relative, basename } from 'path';
import { BuildBuilderOptions, BundleBuilderOptions } from './types';
import { resolve, dirname, relative, basename, join } from 'path';
import { BuildBuilderOptions, PackageBuilderOptions } from './types';
import { statSync } from 'fs';

export interface FileReplacement {
replace: string;
with: string;
}

export interface NormalizedBundleBuilderOptions extends BundleBuilderOptions {
export interface NormalizedBundleBuilderOptions extends PackageBuilderOptions {
entryRoot: string;
projectRoot: string;
assets: NormalizedCopyAssetOption[];
}

export function normalizeBundleOptions(
options: BundleBuilderOptions,
root
export function normalizePackageOptions(
options: PackageBuilderOptions,
root: string,
sourceRoot: string
): NormalizedBundleBuilderOptions {
const entryFile = `${root}/${options.entryFile}`;
const entryRoot = dirname(entryFile);
Expand All @@ -27,6 +29,9 @@ export function normalizeBundleOptions(
...options,
babelConfig: normalizePluginPath(options.babelConfig, root),
rollupConfig: normalizePluginPath(options.rollupConfig, root),
assets: options.assets
? normalizeAssets(options.assets, root, sourceRoot)
: undefined,
entryFile,
entryRoot,
project,
Expand Down Expand Up @@ -62,11 +67,17 @@ function normalizePluginPath(pluginPath: void | string, root: string) {
}
}

function normalizeAssets(
export interface NormalizedCopyAssetOption {
glob: string;
input: string;
output: string;
}

export function normalizeAssets(
assets: any[],
root: string,
sourceRoot: string
): any[] {
): NormalizedCopyAssetOption[] {
return assets.map((asset) => {
if (typeof asset === 'string') {
const assetPath = normalize(asset);
Expand Down

0 comments on commit 8e6ad2f

Please sign in to comment.