feat(react): add assets option for package builder to copy files to output path (#2933)

This commit is contained in:
Jack Hsu 2020-05-01 09:16:49 -04:00 committed by GitHub
parent c4e33123fd
commit 8e6ad2ffab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 184 additions and 52 deletions

View File

@ -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`

View File

@ -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`

View File

@ -1,5 +1,6 @@
import {
checkFilesDoNotExist,
checkFilesExist,
ensureProject,
forEachCli,
readJson,
@ -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', () => {
@ -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', () => {

View File

@ -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",

View File

@ -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",

View File

@ -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;

View File

@ -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';
@ -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';
@ -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;
@ -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,
@ -153,6 +163,12 @@ function createRollupOptions(
);
const plugins = [
copy({
targets: convertCopyAssetsToRollupOptions(
options.outputPath,
options.assets
),
}),
image(),
typescript({
check: true,
@ -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);
@ -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;
}

View File

@ -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": {
@ -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"
}
]
}
}
}

View File

@ -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';
@ -114,8 +114,8 @@ describe('normalizeBuildOptions', () => {
});
});
describe('normalizeBundleOptions', () => {
let testOptions: BundleBuilderOptions;
describe('normalizePackageOptions', () => {
let testOptions: PackageBuilderOptions;
let root: string;
let sourceRoot: Path;
@ -133,18 +133,19 @@ 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);
@ -152,11 +153,11 @@ describe('normalizeBundleOptions', () => {
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('');

View File

@ -1,7 +1,7 @@
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 {
@ -9,14 +9,16 @@ export interface FileReplacement {
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);
@ -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,
@ -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);

View File

@ -45,7 +45,7 @@ export interface Globals {
global: string;
}
export interface BundleBuilderOptions {
export interface PackageBuilderOptions {
outputPath: string;
tsConfig: string;
project: string;
@ -56,4 +56,5 @@ export interface BundleBuilderOptions {
rollupConfig?: string;
babelConfig?: string;
watch?: boolean;
assets?: any[];
}

View File

@ -4583,6 +4583,13 @@
dependencies:
"@types/node" "*"
"@types/fs-extra@^8.0.1":
version "8.1.0"
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.1.0.tgz#1114834b53c3914806cd03b3304b37b3bd221a4d"
integrity sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg==
dependencies:
"@types/node" "*"
"@types/glob@^7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
@ -8212,6 +8219,11 @@ color@^3.0.0:
color-convert "^1.9.1"
color-string "^1.5.2"
colorette@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.1.0.tgz#1f943e5a357fac10b4e0f5aaef3b14cdc1af6ec7"
integrity sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg==
colors@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
@ -12036,6 +12048,20 @@ globalthis@^1.0.0:
function-bind "^1.1.1"
object-keys "^1.0.12"
globby@10.0.1, globby@^10.0.1:
version "10.0.1"
resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.1.tgz#4782c34cb75dd683351335c5829cc3420e606b22"
integrity sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==
dependencies:
"@types/glob" "^7.1.1"
array-union "^2.1.0"
dir-glob "^3.0.1"
fast-glob "^3.0.3"
glob "^7.1.3"
ignore "^5.1.1"
merge2 "^1.2.3"
slash "^3.0.0"
globby@8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.1.tgz#b5ad48b8aa80b35b814fc1281ecc851f1d2b5b50"
@ -12062,20 +12088,6 @@ globby@8.0.2, globby@^8.0.1:
pify "^3.0.0"
slash "^1.0.0"
globby@^10.0.1:
version "10.0.1"
resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.1.tgz#4782c34cb75dd683351335c5829cc3420e606b22"
integrity sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==
dependencies:
"@types/glob" "^7.1.1"
array-union "^2.1.0"
dir-glob "^3.0.1"
fast-glob "^3.0.3"
glob "^7.1.3"
ignore "^5.1.1"
merge2 "^1.2.3"
slash "^3.0.0"
globby@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
@ -20311,6 +20323,17 @@ rollup-plugin-babel@4.3.3:
"@babel/helper-module-imports" "^7.0.0"
rollup-pluginutils "^2.8.1"
rollup-plugin-copy@3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/rollup-plugin-copy/-/rollup-plugin-copy-3.3.0.tgz#5ba230047f86b9f703a29288f242948a5580e7b9"
integrity sha512-euDjCUSBXZa06nqnwCNADbkAcYDfzwowfZQkto9K/TFhiH+QG7I4PUsEMwM9tDgomGWJc//z7KLW8t+tZwxADA==
dependencies:
"@types/fs-extra" "^8.0.1"
colorette "^1.1.0"
fs-extra "^8.1.0"
globby "10.0.1"
is-plain-object "^3.0.0"
rollup-plugin-filesize@6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/rollup-plugin-filesize/-/rollup-plugin-filesize-6.2.1.tgz#552eebc88dd69db3321d99c27dbd49e550812e54"