feat(bundling): move rollup to its own plugin (#12009)

This commit is contained in:
Jack Hsu 2022-09-15 14:55:06 -04:00 committed by GitHub
parent fb25fdada5
commit dd6addefc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 2123 additions and 425 deletions

View File

@ -16,6 +16,10 @@ module.exports = {
scopes: [
{ name: 'angular', description: 'anything Angular specific' },
{ name: 'core', description: 'anything Nx core specific' },
{
name: 'bundling',
description: 'anything bundling specific (e.g. rollup, webpack, etc.)',
},
{ name: 'detox', description: 'anything Detox specific' },
{ name: 'devkit', description: 'devkit-related changes' },
{ name: 'express', description: 'anything Express specific' },

View File

@ -0,0 +1,310 @@
{
"githubRoot": "https://github.com/nrwl/nx/blob/master",
"name": "rollup",
"packageName": "@nrwl/rollup",
"description": "The Nx Plugin for Rollup contains executors and generators that support building applications using Rollup",
"root": "/packages/rollup",
"source": "/packages/rollup/src",
"documentation": [],
"generators": [
{
"name": "init",
"factory": "./src/generators/init/init#rollupInitGenerator",
"schema": {
"$schema": "http://json-schema.org/schema",
"$id": "NxWebpackInit",
"cli": "nx",
"title": "Init Webpack Plugin",
"description": "Init Webpack Plugin.",
"type": "object",
"properties": {
"compiler": {
"type": "string",
"enum": ["babel", "swc", "tsc"],
"description": "The compiler to initialize for.",
"default": "babel"
},
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
"default": false
}
},
"required": [],
"presets": []
},
"description": "Initialize the `@nrwl/rollup` plugin.",
"aliases": ["ng-add"],
"hidden": true,
"implementation": "/packages/rollup/src/generators/init/init#rollupInitGenerator.ts",
"path": "/packages/rollup/src/generators/init/schema.json"
},
{
"name": "rollup-project",
"factory": "./src/generators/rollup-project/rollup-project#rollupProjectGenerator",
"schema": {
"$schema": "http://json-schema.org/schema",
"$id": "NxRollupProject",
"cli": "nx",
"title": "Add Rollup Configuration to a project",
"description": "Add Rollup Configuration to a project.",
"type": "object",
"properties": {
"project": {
"type": "string",
"description": "The name of the project.",
"$default": { "$source": "argv", "index": 0 },
"x-dropdown": "project",
"x-prompt": "What is the name of the project to set up a rollup for?"
},
"compiler": {
"type": "string",
"enum": ["babel", "swc", "tsc"],
"description": "The compiler to use to build source.",
"default": "babel"
},
"main": {
"type": "string",
"description": "Path relative to the workspace root for the main entry file. Defaults to '<projectRoot>/src/main.ts'.",
"alias": "entryFile"
},
"tsConfig": {
"type": "string",
"description": "Path relative to the workspace root for the tsconfig file to build with. Defaults to '<projectRoot>/tsconfig.app.json'."
},
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
"default": false
},
"skipPackageJson": {
"type": "boolean",
"default": false,
"description": "Do not add dependencies to `package.json`."
},
"importPath": {
"type": "string",
"description": "The library name used to import it, like `@myorg/my-awesome-lib`."
},
"external": {
"type": "array",
"description": "A list of external modules that will not be bundled (`react`, `react-dom`, etc.).",
"items": { "type": "string" }
},
"rollupConfig": {
"type": "string",
"description": "Path relative to workspace root to a custom rollup file that takes a config object and returns an updated config."
}
},
"required": [],
"presets": []
},
"description": "Add rollup configuration to a project.",
"hidden": true,
"implementation": "/packages/rollup/src/generators/rollup-project/rollup-project#rollupProjectGenerator.ts",
"aliases": [],
"path": "/packages/rollup/src/generators/rollup-project/schema.json"
}
],
"executors": [
{
"name": "rollup",
"implementation": "/packages/rollup/src/executors/rollup/rollup.impl.ts",
"schema": {
"title": "Web Library Rollup Target (Experimental)",
"description": "Packages a library for different web usages (`UMD`, `ESM`, `CJS`).",
"cli": "nx",
"type": "object",
"properties": {
"project": {
"type": "string",
"description": "The path to package.json file."
},
"main": {
"type": "string",
"description": "The path to the entry file, relative to project.",
"alias": "entryFile",
"x-completion-type": "file",
"x-completion-glob": "**/*@(.js|.ts)"
},
"outputPath": {
"type": "string",
"description": "The output path of the generated files.",
"x-completion-type": "directory"
},
"outputFileName": {
"type": "string",
"description": "Name of the main output file. Defaults same basename as 'main' file."
},
"deleteOutputPath": {
"type": "boolean",
"description": "Delete the output path before building.",
"default": true
},
"tsConfig": {
"type": "string",
"description": "The path to tsconfig file.",
"x-completion-type": "file",
"x-completion-glob": "tsconfig.*.json"
},
"format": {
"type": "array",
"description": "List of module formats to output. Defaults to matching format from tsconfig (e.g. CJS for CommonJS, and ESM otherwise).",
"alias": "f",
"items": { "type": "string", "enum": ["esm", "umd", "cjs"] }
},
"external": {
"type": "array",
"description": "A list of external modules that will not be bundled (`react`, `react-dom`, etc.).",
"items": { "type": "string" }
},
"watch": {
"type": "boolean",
"description": "Enable re-building when files change.",
"default": false
},
"updateBuildableProjectDepsInPackageJson": {
"type": "boolean",
"description": "Update buildable project dependencies in `package.json`.",
"default": true
},
"buildableProjectDepsInPackageJsonType": {
"type": "string",
"description": "When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.",
"enum": ["dependencies", "peerDependencies"],
"default": "peerDependencies"
},
"rollupConfig": {
"oneOf": [
{
"type": "array",
"items": {
"type": "string",
"x-completion-type": "file",
"x-completion-glob": "rollup?(*)@(.js|.ts)"
}
},
{
"type": "string",
"x-completion-type": "file",
"x-completion-glob": "rollup?(*)@(.js|.ts)"
}
],
"description": "Path to a function which takes a rollup config and returns an updated rollup config."
},
"umdName": {
"type": "string",
"description": "The name of your module in `UMD` format. Defaulted to your project name."
},
"globals": {
"description": "A mapping of node modules to their `UMD` global names. Used by the `UMD` bundle.",
"type": "array",
"items": {
"type": "object",
"properties": {
"moduleId": {
"type": "string",
"description": "The node module to map from (e.g. `react-dom`)."
},
"global": {
"type": "string",
"description": "The global name to map to (e.g. `ReactDOM`)."
}
},
"additionalProperties": false,
"required": ["moduleId", "global"]
},
"default": []
},
"extractCss": {
"type": ["boolean", "string"],
"description": "CSS files will be extracted to the output folder. Alternatively custom filename can be provided (e.g. styles.css)",
"default": true
},
"assets": {
"type": "array",
"description": "List of static assets.",
"default": [],
"items": {
"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" }
]
}
},
"compiler": {
"type": "string",
"enum": ["babel", "swc", "tsc"],
"default": "babel",
"description": "Which compiler to use."
},
"javascriptEnabled": {
"type": "boolean",
"description": "Sets `javascriptEnabled` option for less loader",
"default": false
},
"generateExportsField": {
"type": "boolean",
"description": "Generate package.json with 'exports' field. This field defines entry points in the package and is used by Node and the TypeScript compiler.",
"default": false
},
"skipTypeField": {
"type": "boolean",
"description": "Prevents 'type' field from being added to compiled package.json file. Only use this if you are having an issue with this field.",
"default": false
}
},
"required": ["tsConfig", "project", "main", "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" }
]
}
},
"presets": []
},
"description": "Bundle a package using Rollup.",
"aliases": [],
"hidden": false,
"path": "/packages/rollup/src/executors/rollup/schema.json"
}
]
}

View File

@ -294,6 +294,16 @@
]
}
},
{
"name": "rollup",
"packageName": "rollup",
"description": "The Nx Plugin for Rollup contains executors and generators that support building applications using Rollup",
"path": "generated/packages/rollup.json",
"schemas": {
"executors": ["rollup"],
"generators": ["init", "rollup-project"]
}
},
{
"name": "storybook",
"packageName": "storybook",

View File

@ -155,7 +155,7 @@ describe('Next.js Applications', () => {
const appName = uniq('app');
const jsLib = uniq('tslib');
const port = 4201;
const port = 4200;
runCLI(`generate @nrwl/next:app ${appName}`);
runCLI(`generate @nrwl/js:lib ${jsLib} --no-interactive`);
@ -217,12 +217,7 @@ describe('Next.js Applications', () => {
expect(apiData).toContain(`Welcome`);
expect(pageData).toContain(`test value from a file`);
try {
await promisifiedTreeKill(p.pid, 'SIGKILL');
expect(await killPorts(port)).toBeTruthy();
} catch (err) {
expect(err).toBeFalsy();
}
await killPorts();
}, 300_000);
it('should support custom next.config.js and output it in dist', async () => {

11
e2e/rollup/jest.config.ts Normal file
View File

@ -0,0 +1,11 @@
/* eslint-disable */
export default {
transform: {
'^.+\\.[tj]sx?$': 'ts-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
maxWorkers: 1,
globals: { 'ts-jest': { tsconfig: '<rootDir>/tsconfig.spec.json' } },
displayName: 'e2e-rollup',
preset: '../../jest.preset.js',
};

34
e2e/rollup/project.json Normal file
View File

@ -0,0 +1,34 @@
{
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "e2e/rollup",
"projectType": "application",
"targets": {
"e2e": {
"executor": "nx:run-commands",
"options": {
"commands": [
{
"command": "yarn e2e-start-local-registry"
},
{
"command": "yarn e2e-build-package-publish"
},
{
"command": "nx run-e2e-tests e2e-rollup"
}
],
"parallel": false
}
},
"run-e2e-tests": {
"executor": "@nrwl/jest:jest",
"options": {
"jestConfig": "e2e/rollup/jest.config.ts",
"passWithNoTests": true,
"runInBand": true
},
"outputs": ["coverage/e2e/rollup"]
}
},
"implicitDependencies": ["rollup"]
}

View File

@ -0,0 +1,58 @@
import {
cleanupProject,
newProject,
rmDist,
runCLI,
runCommand,
uniq,
updateFile,
updateProjectConfig,
} from '@nrwl/e2e/utils';
describe('Rollup Plugin', () => {
beforeEach(() => newProject());
// afterEach(() => cleanupProject());
it('should be able to setup project to build node programs with rollup and different compilers', async () => {
const myPkg = uniq('my-pkg');
runCLI(`generate @nrwl/js:lib ${myPkg} --buildable=false`);
updateFile(`libs/${myPkg}/src/index.ts`, `console.log('Hello');\n`);
// babel (default)
runCLI(
`generate @nrwl/rollup:rollup-project ${myPkg} --target=node --tsConfig=libs/${myPkg}/tsconfig.lib.json --main=libs/${myPkg}/src/index.ts`
);
rmDist();
runCLI(`build ${myPkg}`);
let output = runCommand(`node dist/libs/${myPkg}/index.cjs`);
expect(output).toMatch(/Hello/);
updateProjectConfig(myPkg, (config) => {
delete config.targets.build;
return config;
});
// swc
runCLI(
`generate @nrwl/rollup:rollup-project ${myPkg} --target=node --tsConfig=libs/${myPkg}/tsconfig.lib.json --main=libs/${myPkg}/src/index.ts --compiler=swc`
);
rmDist();
runCLI(`build ${myPkg}`);
output = runCommand(`node dist/libs/${myPkg}/index.cjs`);
expect(output).toMatch(/Hello/);
updateProjectConfig(myPkg, (config) => {
delete config.targets.build;
return config;
});
// tsc
runCLI(
`generate @nrwl/rollup:rollup-project ${myPkg} --target=node --tsConfig=libs/${myPkg}/tsconfig.lib.json --main=libs/${myPkg}/src/index.ts --compiler=tsc`
);
rmDist();
runCLI(`build ${myPkg}`);
output = runCommand(`node dist/libs/${myPkg}/index.cjs`);
expect(output).toMatch(/Hello/);
}, 500000);
});

13
e2e/rollup/tsconfig.json Normal file
View File

@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"types": ["node", "jest"]
},
"include": [],
"files": [],
"references": [
{
"path": "./tsconfig.spec.json"
}
]
}

View File

@ -0,0 +1,20 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": [
"**/*.test.ts",
"**/*.spec.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"**/*.d.ts",
"jest.config.ts"
]
}

View File

@ -313,6 +313,7 @@ export function newProject({
`@nrwl/next`,
`@nrwl/node`,
`@nrwl/nx-plugin`,
`@nrwl/rollup`,
`@nrwl/react`,
`@nrwl/storybook`,
`@nrwl/web`,

View File

@ -33,8 +33,8 @@ describe('file-server', () => {
try {
await promisifiedTreeKill(p.pid, 'SIGKILL');
await killPorts(port);
} catch (err) {
expect(err).toBeFalsy();
} catch {
// ignore
}
}, 1000000);
});

View File

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="100 100 800 800"
id="svg61"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs65" />
<style
id="style2">.st0{fill:url(#XMLID_4_);} .st1{fill:url(#XMLID_5_);} .st2{fill:url(#XMLID_8_);} .st3{fill:url(#XMLID_9_);} .st4{fill:url(#XMLID_11_);} .st5{opacity:0.3;fill:url(#XMLID_16_);}</style>
<g
id="XMLID_14_">
<linearGradient
id="XMLID_4_"
gradientUnits="userSpaceOnUse"
x1="444.469"
y1="526.051"
x2="598.469"
y2="562.051">
<stop
offset="0"
stop-color="#000000"
id="stop4" />
<stop
offset=".157"
stop-color="#000000"
id="stop6" />
<stop
offset=".434"
stop-color="#000000"
id="stop8" />
<stop
offset=".714"
stop-color="#000000"
id="stop10" />
<stop
offset="1"
stop-color="#000000"
id="stop12" />
</linearGradient>
<path
id="XMLID_15_"
class="st0"
d="M721 410c0-33.6-8.8-65.1-24.3-92.4-41.1-42.3-130.5-52.1-152.7-.2-22.8 53.2 38.3 112.4 65 107.7 34-6-6-84-6-84 52 98 40 68-54 158S359 779 345 787c-.6.4-1.2.7-1.9 1h368.7c6.5 0 10.7-6.9 7.8-12.7l-96.4-190.8c-2.1-4.1-.6-9.2 3.4-11.5C683 540.6 721 479.8 721 410z" />
</g>
<g
id="XMLID_2_">
<linearGradient
id="XMLID_5_"
gradientUnits="userSpaceOnUse"
x1="420.382"
y1="475.002"
x2="696.383"
y2="689.002">
<stop
offset="0"
stop-color="#000"
id="stop17" />
<stop
offset="1"
stop-color="#000000"
id="stop19" />
</linearGradient>
<path
id="XMLID_10_"
class="st1"
d="M721 410c0-33.6-8.8-65.1-24.3-92.4-41.1-42.3-130.5-52.1-152.7-.2-22.8 53.2 38.3 112.4 65 107.7 34-6-6-84-6-84 52 98 40 68-54 158S359 779 345 787c-.6.4-1.2.7-1.9 1h368.7c6.5 0 10.7-6.9 7.8-12.7l-96.4-190.8c-2.1-4.1-.6-9.2 3.4-11.5C683 540.6 721 479.8 721 410z" />
</g>
<linearGradient
id="XMLID_8_"
gradientUnits="userSpaceOnUse"
x1="429.386"
y1="517.156"
x2="469.386"
y2="559.156">
<stop
offset="0"
stop-color="#000000"
id="stop24" />
<stop
offset=".157"
stop-color="#000000"
id="stop26" />
<stop
offset=".434"
stop-color="#000000"
id="stop28" />
<stop
offset=".714"
stop-color="#000000"
id="stop30" />
<stop
offset="1"
stop-color="#000000"
id="stop32" />
</linearGradient>
<path
id="XMLID_3_"
class="st2"
d="M345 787c14-8 110-198 204-288s106-60 54-158c0 0-199 279-271 417" />
<g
id="XMLID_7_">
<linearGradient
id="XMLID_9_"
gradientUnits="userSpaceOnUse"
x1="502.111"
y1="589.457"
x2="490.111"
y2="417.457">
<stop
offset="0"
stop-color="#000000"
id="stop36" />
<stop
offset=".157"
stop-color="#000000"
id="stop38" />
<stop
offset=".434"
stop-color="#000000"
id="stop40" />
<stop
offset=".714"
stop-color="#000000"
id="stop42" />
<stop
offset="1"
stop-color="#000000"
id="stop44" />
</linearGradient>
<path
id="XMLID_12_"
class="st3"
d="M373 537c134.4-247.1 152-272 222-272 36.8 0 73.9 16.6 97.9 46.1-32.7-52.7-90.6-88-156.9-89H307.7c-4.8 0-8.7 3.9-8.7 8.7V691c13.6-35.1 36.7-85.3 74-154z" />
</g>
<linearGradient
id="XMLID_11_"
gradientUnits="userSpaceOnUse"
x1="450.125"
y1="514.209"
x2="506.943"
y2="552.846">
<stop
offset="0"
stop-color="#FFFFFF"
id="stop49" />
<stop
offset="1"
stop-color="#FFFFFF"
id="stop51" />
</linearGradient>
<path
id="XMLID_6_"
class="st4"
d="M549 499c-94 90-190 280-204 288s-37.5 9-50-5c-13.3-14.9-34-39 78-245 134.4-247.1 152-272 222-272 36.8 0 73.9 16.6 97.9 46.1 1.3 2.1 2.6 4.3 3.9 6.5-41.1-42.3-130.5-52.1-152.7-.2-22.8 53.2 38.3 112.4 65 107.7 34-6-6-84-6-84C655 439 643 409 549 499z" />
<linearGradient
id="XMLID_16_"
gradientUnits="userSpaceOnUse"
x1="508.333"
y1="295.758"
x2="450.333"
y2="933.758">
<stop
offset="0"
stop-color="#FFF"
id="stop55" />
<stop
offset="1"
stop-color="#FFF"
stop-opacity="0"
id="stop57" />
</linearGradient>
<path
id="XMLID_13_"
class="st5"
d="M384 548c134.4-247.1 152-272 222-272 30.3 0 60.8 11.3 84 31.7-24-27.4-59.6-42.7-95-42.7-70 0-87.6 24.9-222 272-112 206-91.3 230.1-78 245 1.9 2.1 4.1 3.9 6.4 5.4-11.7-17-16.9-56.5 82.6-239.4z" />
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -16,6 +16,7 @@ export const iconsMap: Record<string, string> = {
'nx-plugin': '/images/icons/nx.svg',
react: '/images/icons/react.svg',
'react-native': '/images/icons/react.svg',
rollup: '/images/icons/rollup.svg',
storybook: '/images/icons/storybook.svg',
web: '/images/icons/html5.svg',
webpack: '/images/icons/webpack.svg',

View File

@ -0,0 +1,25 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
},
{
"files": ["./package.json", "./generators.json", "./executors.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nrwl/nx/nx-plugin-checks": "error"
}
}
]
}

13
packages/rollup/README.md Normal file
View File

@ -0,0 +1,13 @@
<p style="text-align: center;"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx.png" width="600" alt="Nx - Smart, Fast and Extensible Build System"></p>
{{links}}
<hr>
# Nx: Smart, Fast and Extensible Build System
Nx is a next generation build system with first class monorepo support and powerful integrations.
This package is a [Rollup plugin for Nx](https://nx.dev/packages/rollup).
{{content}}

View File

@ -0,0 +1,16 @@
{
"builders": {
"rollup": {
"implementation": "./src/executors/rollup/compat",
"schema": "./src/executors/rollup/schema.json",
"description": "Bundle a package using Rollup."
}
},
"executors": {
"rollup": {
"implementation": "./src/executors/rollup/rollup.impl",
"schema": "./src/executors/rollup/schema.json",
"description": "Bundle a package using Rollup."
}
}
}

View File

@ -0,0 +1,33 @@
{
"name": "Nx rollup",
"version": "0.1",
"schematics": {
"init": {
"factory": "./src/generators/init/init#rollupInitSchematic",
"schema": "./src/generators/init/schema.json",
"description": "Initialize the `@nrwl/rollup` plugin.",
"hidden": true
},
"rollup-project": {
"factory": "./src/generators/rollup-project/rollup-project#rollupProjectSchematic",
"schema": "./src/generators/rollup-project/schema.json",
"description": "Add rollup configuration to a project.",
"hidden": true
}
},
"generators": {
"init": {
"factory": "./src/generators/init/init#rollupInitGenerator",
"schema": "./src/generators/init/schema.json",
"description": "Initialize the `@nrwl/rollup` plugin.",
"aliases": ["ng-add"],
"hidden": true
},
"rollup-project": {
"factory": "./src/generators/rollup-project/rollup-project#rollupProjectGenerator",
"schema": "./src/generators/rollup-project/schema.json",
"description": "Add rollup configuration to a project.",
"hidden": true
}
}
}

2
packages/rollup/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './src/executors/rollup/schema';
export * from './src/executors/rollup/rollup.impl';

View File

@ -0,0 +1,11 @@
/* eslint-disable */
export default {
transform: {
'^.+\\.[tj]sx?$': 'ts-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
globals: { 'ts-jest': { tsconfig: '<rootDir>/tsconfig.spec.json' } },
displayName: 'rollup',
testEnvironment: 'node',
preset: '../../jest.preset.js',
};

View File

@ -0,0 +1,52 @@
{
"name": "@nrwl/rollup",
"version": "0.0.1",
"description": "The Nx Plugin for Rollup contains executors and generators that support building applications using Rollup",
"repository": {
"type": "git",
"url": "https://github.com/nrwl/nx.git",
"directory": "packages/rollup"
},
"keywords": [
"Monorepo",
"Rollup",
"Web",
"CLI"
],
"main": "./index.js",
"typings": "./index.d.ts",
"author": "Victor Savkin",
"license": "MIT",
"bugs": {
"url": "https://github.com/nrwl/nx/issues"
},
"homepage": "https://nx.dev",
"schematics": "./generators.json",
"builders": "./executors.json",
"ng-update": {
"requirements": {},
"migrations": "./migrations.json"
},
"dependencies": {
"@nrwl/devkit": "file:../devkit",
"@nrwl/js": "file:../js",
"@nrwl/workspace": "file:../workspace",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^20.0.0",
"@rollup/plugin-image": "^2.1.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.0.4",
"autoprefixer": "^10.4.9",
"babel-plugin-transform-async-to-promises": "^0.8.15",
"chalk": "4.1.0",
"fs-extra": "^10.1.0",
"postcss": "^8.4.14",
"rollup": "^2.56.2",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-postcss": "^4.0.1",
"rollup-plugin-typescript2": "^0.31.1",
"rxjs": "^6.5.4",
"tslib": "^2.3.0"
}
}

View File

@ -0,0 +1,87 @@
{
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/rollup",
"projectType": "library",
"targets": {
"test": {
"executor": "@nrwl/jest:jest",
"options": {
"jestConfig": "packages/rollup/jest.config.ts",
"passWithNoTests": true
},
"outputs": ["coverage/packages/rollup"]
},
"build-base": {
"executor": "@nrwl/js:tsc",
"options": {
"outputPath": "build/packages/rollup",
"tsConfig": "packages/rollup/tsconfig.lib.json",
"main": "packages/rollup/index.ts",
"updateBuildableProjectDepsInPackageJson": false,
"assets": [
{
"input": "packages/rollup",
"glob": "**/files/**",
"output": "/"
},
{
"input": "packages/rollup",
"glob": "**/files/**/.gitkeep",
"output": "/"
},
{
"input": "packages/rollup",
"glob": "**/*.json",
"ignore": ["**/tsconfig*.json", "project.json", ".eslintrc.json"],
"output": "/"
},
{
"input": "packages/rollup",
"glob": "**/*.js",
"ignore": ["**/jest.config.js"],
"output": "/"
},
{
"input": "packages/rollup",
"glob": "**/*.d.ts",
"output": "/"
},
{
"input": "",
"glob": "LICENSE",
"output": "/"
}
]
},
"outputs": ["{options.outputPath}"]
},
"build": {
"executor": "nx:run-commands",
"outputs": ["build/packages/rollup"],
"options": {
"command": "node ./scripts/copy-readme.js rollup"
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"packages/rollup/**/*.ts",
"packages/rollup/**/*.spec.ts",
"packages/rollup/**/*_spec.ts",
"packages/rollup/**/*.spec.tsx",
"packages/rollup/**/*.spec.js",
"packages/rollup/**/*.spec.jsx",
"packages/rollup/**/*.d.ts",
"packages/rollup/**/executors/**/schema.json",
"packages/rollup/**/generators/**/schema.json",
"packages/rollup/generators.json",
"packages/rollup/executors.json",
"packages/rollup/package.json",
"packages/rollup/migrations.json"
]
},
"outputs": ["{options.outputFile}"]
}
}
}

View File

@ -0,0 +1,5 @@
import { convertNxExecutor } from '@nrwl/devkit';
import rollupExecutor from './rollup.impl';
export default convertNxExecutor(rollupExecutor);

View File

@ -1,8 +1,8 @@
import { normalizeWebRollupOptions } from './normalize';
import { WebRollupOptions } from '../schema';
import { normalizeRollupExecutorOptions } from './normalize';
import { RollupExecutorOptions } from '../schema';
describe('normalizeWebRollupOptions', () => {
let testOptions: WebRollupOptions;
describe('normalizeRollupExecutorOptions', () => {
let testOptions: RollupExecutorOptions;
let root: string;
let sourceRoot: string;
@ -10,7 +10,7 @@ describe('normalizeWebRollupOptions', () => {
testOptions = {
outputPath: '/tmp',
project: 'apps/nodeapp/package.json',
entryFile: 'apps/nodeapp/src/main.ts',
main: 'apps/nodeapp/src/main.ts',
tsConfig: 'apps/nodeapp/tsconfig.app.json',
rollupConfig: 'apps/nodeapp/rollup.config',
format: ['esm'],
@ -20,10 +20,10 @@ describe('normalizeWebRollupOptions', () => {
});
it('should resolve both node modules and relative path for rollupConfig', () => {
let result = normalizeWebRollupOptions(testOptions, root, sourceRoot);
let result = normalizeRollupExecutorOptions(testOptions, root, sourceRoot);
expect(result.rollupConfig).toEqual(['/root/apps/nodeapp/rollup.config']);
result = normalizeWebRollupOptions(
result = normalizeRollupExecutorOptions(
{
...testOptions,
// something that exists in node_modules
@ -40,7 +40,11 @@ describe('normalizeWebRollupOptions', () => {
it('should handle rollupConfig being undefined', () => {
delete testOptions.rollupConfig;
const result = normalizeWebRollupOptions(testOptions, root, sourceRoot);
const result = normalizeRollupExecutorOptions(
testOptions,
root,
sourceRoot
);
expect(result.rollupConfig).toEqual([]);
});
});

View File

@ -0,0 +1,104 @@
import { basename, dirname, relative, resolve } from 'path';
import { statSync } from 'fs';
import { normalizePath } from '@nrwl/devkit';
import type { AssetGlobPattern, RollupExecutorOptions } from '../schema';
export interface NormalizedRollupExecutorOptions extends RollupExecutorOptions {
entryRoot: string;
projectRoot: string;
assets: AssetGlobPattern[];
rollupConfig: string[];
}
export function normalizeRollupExecutorOptions(
options: RollupExecutorOptions,
root: string,
sourceRoot: string
): NormalizedRollupExecutorOptions {
const main = `${root}/${options.main}`;
const entryRoot = dirname(main);
const project = `${root}/${options.project}`;
const projectRoot = dirname(project);
const outputPath = `${root}/${options.outputPath}`;
if (options.buildableProjectDepsInPackageJsonType == undefined) {
options.buildableProjectDepsInPackageJsonType = 'peerDependencies';
}
return {
...options,
// de-dupe formats
format: Array.from(new Set(options.format)),
rollupConfig: []
.concat(options.rollupConfig)
.filter(Boolean)
.map((p) => normalizePluginPath(p, root)),
assets: options.assets
? normalizeAssets(options.assets, root, sourceRoot)
: undefined,
main,
entryRoot,
project,
projectRoot,
outputPath,
};
}
export function normalizePluginPath(pluginPath: void | string, root: string) {
if (!pluginPath) {
return '';
}
try {
return require.resolve(pluginPath);
} catch {
return resolve(root, pluginPath);
}
}
export function normalizeAssets(
assets: any[],
root: string,
sourceRoot: string
): AssetGlobPattern[] {
return assets.map((asset) => {
if (typeof asset === 'string') {
const assetPath = normalizePath(asset);
const resolvedAssetPath = resolve(root, assetPath);
const resolvedSourceRoot = resolve(root, sourceRoot);
if (!resolvedAssetPath.startsWith(resolvedSourceRoot)) {
throw new Error(
`The ${resolvedAssetPath} asset path must start with the project source root: ${sourceRoot}`
);
}
const isDirectory = statSync(resolvedAssetPath).isDirectory();
const input = isDirectory
? resolvedAssetPath
: dirname(resolvedAssetPath);
const output = relative(resolvedSourceRoot, resolve(root, input));
const glob = isDirectory ? '**/*' : basename(resolvedAssetPath);
return {
input,
output,
glob,
};
} else {
if (asset.output.startsWith('..')) {
throw new Error(
'An asset cannot be written to a location outside of the output path.'
);
}
const assetPath = normalizePath(asset.input);
const resolvedAssetPath = resolve(root, assetPath);
return {
...asset,
input: resolvedAssetPath,
// Now we remove starting slash to make Webpack place it from the output root.
output: asset.output.replace(/^\//, ''),
};
}
});
}

View File

@ -7,7 +7,7 @@ describe('updatePackageJson', () => {
outputPath: 'dist/index.js',
tsConfig: './tsconfig.json',
project: './package.json',
entryFile: './index.js',
main: './index.js',
entryRoot: '.',
projectRoot: '.',
assets: [],

View File

@ -7,10 +7,10 @@ import {
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
import { writeJsonFile } from 'nx/src/utils/fileutils';
import { PackageJson } from 'nx/src/utils/package-json';
import { NormalizedWebRollupOptions } from './normalize';
import { NormalizedRollupExecutorOptions } from './normalize';
export function updatePackageJson(
options: NormalizedWebRollupOptions,
options: NormalizedRollupExecutorOptions,
context: ExecutorContext,
target: ProjectGraphProjectNode,
dependencies: DependentBuildableProjectNode[],
@ -20,7 +20,7 @@ export function updatePackageJson(
const hasCjsFormat =
options.format.includes('umd') || options.format.includes('cjs');
const types = `./${relative(options.entryRoot, options.entryFile).replace(
const types = `./${relative(options.entryRoot, options.main).replace(
/\.[jt]sx?$/,
'.d.ts'
)}`;

View File

@ -1,14 +1,14 @@
import { ExecutorContext } from '@nrwl/devkit';
import * as rollup from 'rollup';
import { WebRollupOptions } from './schema';
import { RollupExecutorOptions } from './schema';
import { createRollupOptions } from './rollup.impl';
import { normalizeWebRollupOptions } from './lib/normalize';
import { normalizeRollupExecutorOptions } from './lib/normalize';
jest.mock('rollup-plugin-copy', () => jest.fn());
describe('rollupExecutor', () => {
let context: ExecutorContext;
let testOptions: WebRollupOptions;
let testOptions: RollupExecutorOptions;
beforeEach(async () => {
context = {
@ -25,7 +25,7 @@ describe('rollupExecutor', () => {
};
testOptions = {
compiler: 'babel',
entryFile: 'libs/ui/src/index.ts',
main: 'libs/ui/src/index.ts',
outputPath: 'dist/ui',
project: 'libs/ui/package.json',
tsConfig: 'libs/ui/tsconfig.json',
@ -37,7 +37,7 @@ describe('rollupExecutor', () => {
describe('createRollupOptions', () => {
it('should create rollup options for valid config', () => {
const result: any = createRollupOptions(
normalizeWebRollupOptions(testOptions, '/root', '/root/src'),
normalizeRollupExecutorOptions(testOptions, '/root', '/root/src'),
[],
context,
{ name: 'example' },
@ -74,7 +74,7 @@ describe('rollupExecutor', () => {
{ virtual: true }
);
const result: any = createRollupOptions(
normalizeWebRollupOptions(
normalizeRollupExecutorOptions(
{ ...testOptions, rollupConfig: 'custom-rollup.config.ts' },
'/root',
'/root/src'
@ -104,7 +104,7 @@ describe('rollupExecutor', () => {
{ virtual: true }
);
const result: any = createRollupOptions(
normalizeWebRollupOptions(
normalizeRollupExecutorOptions(
{
...testOptions,
rollupConfig: [
@ -131,7 +131,7 @@ describe('rollupExecutor', () => {
it(`should always use forward slashes for asset paths`, () => {
createRollupOptions(
{
...normalizeWebRollupOptions(testOptions, '/root', '/root/src'),
...normalizeRollupExecutorOptions(testOptions, '/root', '/root/src'),
assets: [
{
glob: 'README.md',
@ -154,7 +154,7 @@ describe('rollupExecutor', () => {
it(`should treat npm dependencies as external`, () => {
const options = createRollupOptions(
normalizeWebRollupOptions(testOptions, '/root', '/root/src'),
normalizeRollupExecutorOptions(testOptions, '/root', '/root/src'),
[],
context,
{ name: 'example' },

View File

@ -0,0 +1,367 @@
import * as ts from 'typescript';
import * as rollup from 'rollup';
import * as peerDepsExternal from 'rollup-plugin-peer-deps-external';
import { getBabelInputPlugin } from '@rollup/plugin-babel';
import { dirname, join, parse } from 'path';
import { from, Observable, of } from 'rxjs';
import { catchError, concatMap, last, scan, tap } from 'rxjs/operators';
import { eachValueFrom } from '@nrwl/devkit/src/utils/rxjs-for-await';
import * as autoprefixer from 'autoprefixer';
import type { ExecutorContext } from '@nrwl/devkit';
import { joinPathFragments, logger, names, readJsonFile } from '@nrwl/devkit';
import {
calculateProjectDependencies,
computeCompilerOptionsPaths,
DependentBuildableProjectNode,
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
import resolve from '@rollup/plugin-node-resolve';
import { AssetGlobPattern, RollupExecutorOptions } from './schema';
import { runRollup } from './lib/run-rollup';
import {
NormalizedRollupExecutorOptions,
normalizeRollupExecutorOptions,
} from './lib/normalize';
import { analyze } from './lib/analyze-plugin';
import { deleteOutputDir } from '../../utils/fs';
import { swc } from './lib/swc-plugin';
import { validateTypes } from './lib/validate-types';
import { updatePackageJson } from './lib/update-package-json';
// These use require because the ES import isn't correct.
const commonjs = require('@rollup/plugin-commonjs');
const image = require('@rollup/plugin-image');
const json = require('@rollup/plugin-json');
const copy = require('rollup-plugin-copy');
const postcss = require('rollup-plugin-postcss');
const fileExtensions = ['.js', '.jsx', '.ts', '.tsx'];
export async function* rollupExecutor(
rawOptions: RollupExecutorOptions,
context: ExecutorContext
) {
process.env.NODE_ENV ??= 'production';
const project = context.workspace.projects[context.projectName];
const sourceRoot = project.sourceRoot;
const { target, dependencies } = calculateProjectDependencies(
context.projectGraph,
context.root,
context.projectName,
context.targetName,
context.configurationName,
true
);
const options = normalizeRollupExecutorOptions(
rawOptions,
context.root,
sourceRoot
);
// TODO(jack): Remove UMD in Nx 15
if (options.format.includes('umd')) {
if (options.format.includes('cjs')) {
throw new Error(
'Cannot use both UMD and CJS. We recommend you use ESM or CJS.'
);
} else {
logger.warn('UMD format is deprecated and will be removed in Nx 15');
}
}
const packageJson = readJsonFile(options.project);
const npmDeps = (context.projectGraph.dependencies[context.projectName] ?? [])
.filter((d) => d.target.startsWith('npm:'))
.map((d) => d.target.slice(4));
const rollupOptions = createRollupOptions(
options,
dependencies,
context,
packageJson,
sourceRoot,
npmDeps
);
if (options.compiler === 'swc') {
try {
await validateTypes({
workspaceRoot: context.root,
projectRoot: options.projectRoot,
tsconfig: options.tsConfig,
});
} catch {
return { success: false };
}
}
if (options.watch) {
const watcher = rollup.watch(rollupOptions);
return yield* eachValueFrom(
new Observable<{ success: boolean }>((obs) => {
watcher.on('event', (data) => {
if (data.code === 'START') {
logger.info(`Bundling ${context.projectName}...`);
} else if (data.code === 'END') {
updatePackageJson(
options,
context,
target,
dependencies,
packageJson
);
logger.info('Bundle complete. Watching for file changes...');
obs.next({ success: true });
} else if (data.code === 'ERROR') {
logger.error(`Error during bundle: ${data.error.message}`);
obs.next({ success: false });
}
});
// Teardown logic. Close watcher when unsubscribed.
return () => watcher.close();
})
);
} else {
logger.info(`Bundling ${context.projectName}...`);
// Delete output path before bundling
if (options.deleteOutputPath) {
deleteOutputDir(context.root, options.outputPath);
}
const start = process.hrtime.bigint();
return from(rollupOptions)
.pipe(
concatMap((opts) =>
runRollup(opts).pipe(
catchError((e) => {
logger.error(`Error during bundle: ${e}`);
return of({ success: false });
})
)
),
scan(
(acc, result) => {
if (!acc.success) return acc;
return result;
},
{ success: true }
),
last(),
tap({
next: (result) => {
if (result.success) {
const end = process.hrtime.bigint();
const duration = `${(Number(end - start) / 1_000_000_000).toFixed(
2
)}s`;
updatePackageJson(
options,
context,
target,
dependencies,
packageJson
);
logger.info(`⚡ Done in ${duration}`);
} else {
logger.error(`Bundle failed: ${context.projectName}`);
}
},
})
)
.toPromise();
}
}
// -----------------------------------------------------------------------------
export function createRollupOptions(
options: NormalizedRollupExecutorOptions,
dependencies: DependentBuildableProjectNode[],
context: ExecutorContext,
packageJson: any,
sourceRoot: string,
npmDeps: string[]
): rollup.InputOptions[] {
const useBabel = options.compiler === 'babel';
const useTsc = options.compiler === 'tsc';
const useSwc = options.compiler === 'swc';
const tsConfigPath = joinPathFragments(context.root, options.tsConfig);
const configFile = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
const config = ts.parseJsonConfigFileContent(
configFile.config,
ts.sys,
dirname(tsConfigPath)
);
if (!options.format || !options.format.length) {
options.format = readCompatibleFormats(config);
}
return options.format.map((format, idx) => {
const plugins = [
copy({
targets: convertCopyAssetsToRollupOptions(
options.outputPath,
options.assets
),
}),
image(),
json(),
(useTsc || useBabel) &&
require('rollup-plugin-typescript2')({
check: true,
tsconfig: options.tsConfig,
tsconfigOverride: {
compilerOptions: createTsCompilerOptions(
config,
dependencies,
options
),
},
}),
peerDepsExternal({
packageJsonPath: options.project,
}),
postcss({
inject: true,
extract: options.extractCss,
autoModules: true,
plugins: [autoprefixer],
use: {
less: {
javascriptEnabled: options.javascriptEnabled,
},
},
}),
resolve({
preferBuiltins: true,
extensions: fileExtensions,
}),
useSwc && swc(),
useBabel &&
getBabelInputPlugin({
// Let's `@nrwl/web/babel` preset know that we are packaging.
caller: {
// @ts-ignore
// Ignoring type checks for caller since we have custom attributes
isNxPackage: true,
// Always target esnext and let rollup handle cjs/umd
supportsStaticESM: true,
isModern: true,
},
cwd: join(context.root, sourceRoot),
rootMode: 'upward',
babelrc: true,
extensions: fileExtensions,
babelHelpers: 'bundled',
skipPreflightCheck: true, // pre-flight check may yield false positives and also slows down the build
exclude: /node_modules/,
plugins: [
format === 'esm'
? undefined
: require.resolve('babel-plugin-transform-async-to-promises'),
].filter(Boolean),
}),
commonjs(),
analyze(),
];
const globals = options.globals
? options.globals.reduce(
(acc, item) => {
acc[item.moduleId] = item.global;
return acc;
},
{ 'react/jsx-runtime': 'jsxRuntime' }
)
: { 'react/jsx-runtime': 'jsxRuntime' };
const externalPackages = dependencies
.map((d) => d.name)
.concat(options.external || [])
.concat(Object.keys(packageJson.dependencies || {}));
const rollupConfig = {
input: options.outputFileName
? {
[parse(options.outputFileName).name]: options.main,
}
: options.main,
output: {
globals,
format,
dir: `${options.outputPath}`,
name: options.umdName || names(context.projectName).className,
entryFileNames: `[name].${format === 'esm' ? 'js' : 'cjs'}`,
chunkFileNames: `[name].${format === 'esm' ? 'js' : 'cjs'}`,
// umd doesn't support code-split bundles
inlineDynamicImports: format === 'umd',
},
external: (id) =>
externalPackages.some(
(name) => id === name || id.startsWith(`${name}/`)
) || npmDeps.some((name) => id === name || id.startsWith(`${name}/`)), // Could be a deep import
plugins,
};
return options.rollupConfig.reduce((currentConfig, plugin) => {
return require(plugin)(currentConfig, options);
}, rollupConfig);
});
}
function createTsCompilerOptions(
config: ts.ParsedCommandLine,
dependencies,
options
) {
const compilerOptionPaths = computeCompilerOptionsPaths(config, dependencies);
const compilerOptions = {
rootDir: options.entryRoot,
allowJs: false,
declaration: true,
paths: compilerOptionPaths,
};
if (config.options.module === ts.ModuleKind.CommonJS) {
compilerOptions['module'] = 'ESNext';
}
return compilerOptions;
}
interface RollupCopyAssetOption {
src: string;
dest: string;
}
function convertCopyAssetsToRollupOptions(
outputPath: string,
assets: AssetGlobPattern[]
): RollupCopyAssetOption[] {
return assets
? assets.map((a) => ({
src: join(a.input, a.glob).replace(/\\/g, '/'),
dest: join(outputPath, a.output).replace(/\\/g, '/'),
}))
: undefined;
}
function readCompatibleFormats(config: ts.ParsedCommandLine) {
switch (config.options.module) {
case ts.ModuleKind.CommonJS:
return ['cjs'];
case ts.ModuleKind.UMD:
case ts.ModuleKind.AMD:
return ['umd'];
default:
return ['esm'];
}
}
export default rollupExecutor;

View File

@ -0,0 +1,37 @@
type Compiler = 'babel' | 'swc';
export interface AssetGlobPattern {
glob: string;
input: string;
output: string;
ignore?: string[];
}
export interface Globals {
moduleId: string;
global: string;
}
export interface RollupExecutorOptions {
outputPath: string;
tsConfig: string;
project: string;
main: string;
outputFileName?: string;
extractCss?: boolean | string;
globals?: Globals[];
external?: string[];
rollupConfig?: string | string[];
watch?: boolean;
assets?: any[];
updateBuildableProjectDepsInPackageJson?: boolean;
buildableProjectDepsInPackageJsonType?: 'dependencies' | 'peerDependencies';
umdName?: string;
deleteOutputPath?: boolean;
format?: string[];
compiler?: 'babel' | 'tsc' | 'swc';
javascriptEnabled?: boolean;
// TODO(jack): remove this for Nx 15
skipTypeField?: boolean;
generateExportsField?: boolean;
}

View File

@ -0,0 +1,176 @@
{
"title": "Web Library Rollup Target (Experimental)",
"description": "Packages a library for different web usages (`UMD`, `ESM`, `CJS`).",
"cli": "nx",
"type": "object",
"properties": {
"project": {
"type": "string",
"description": "The path to package.json file."
},
"main": {
"type": "string",
"description": "The path to the entry file, relative to project.",
"alias": "entryFile",
"x-completion-type": "file",
"x-completion-glob": "**/*@(.js|.ts)"
},
"outputPath": {
"type": "string",
"description": "The output path of the generated files.",
"x-completion-type": "directory"
},
"outputFileName": {
"type": "string",
"description": "Name of the main output file. Defaults same basename as 'main' file."
},
"deleteOutputPath": {
"type": "boolean",
"description": "Delete the output path before building.",
"default": true
},
"tsConfig": {
"type": "string",
"description": "The path to tsconfig file.",
"x-completion-type": "file",
"x-completion-glob": "tsconfig.*.json"
},
"format": {
"type": "array",
"description": "List of module formats to output. Defaults to matching format from tsconfig (e.g. CJS for CommonJS, and ESM otherwise).",
"alias": "f",
"items": {
"type": "string",
"enum": ["esm", "umd", "cjs"]
}
},
"external": {
"type": "array",
"description": "A list of external modules that will not be bundled (`react`, `react-dom`, etc.).",
"items": {
"type": "string"
}
},
"watch": {
"type": "boolean",
"description": "Enable re-building when files change.",
"default": false
},
"updateBuildableProjectDepsInPackageJson": {
"type": "boolean",
"description": "Update buildable project dependencies in `package.json`.",
"default": true
},
"buildableProjectDepsInPackageJsonType": {
"type": "string",
"description": "When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.",
"enum": ["dependencies", "peerDependencies"],
"default": "peerDependencies"
},
"rollupConfig": {
"oneOf": [
{
"type": "array",
"items": {
"type": "string",
"x-completion-type": "file",
"x-completion-glob": "rollup?(*)@(.js|.ts)"
}
},
{
"type": "string",
"x-completion-type": "file",
"x-completion-glob": "rollup?(*)@(.js|.ts)"
}
],
"description": "Path to a function which takes a rollup config and returns an updated rollup config."
},
"umdName": {
"type": "string",
"description": "The name of your module in `UMD` format. Defaulted to your project name."
},
"globals": {
"description": "A mapping of node modules to their `UMD` global names. Used by the `UMD` bundle.",
"type": "array",
"items": {
"type": "object",
"properties": {
"moduleId": {
"type": "string",
"description": "The node module to map from (e.g. `react-dom`)."
},
"global": {
"type": "string",
"description": "The global name to map to (e.g. `ReactDOM`)."
}
},
"additionalProperties": false,
"required": ["moduleId", "global"]
},
"default": []
},
"extractCss": {
"type": ["boolean", "string"],
"description": "CSS files will be extracted to the output folder. Alternatively custom filename can be provided (e.g. styles.css)",
"default": true
},
"assets": {
"type": "array",
"description": "List of static assets.",
"default": [],
"items": {
"$ref": "#/definitions/assetPattern"
}
},
"compiler": {
"type": "string",
"enum": ["babel", "swc", "tsc"],
"default": "babel",
"description": "Which compiler to use."
},
"javascriptEnabled": {
"type": "boolean",
"description": "Sets `javascriptEnabled` option for less loader",
"default": false
},
"generateExportsField": {
"type": "boolean",
"description": "Generate package.json with 'exports' field. This field defines entry points in the package and is used by Node and the TypeScript compiler.",
"default": false
},
"skipTypeField": {
"type": "boolean",
"description": "Prevents 'type' field from being added to compiled package.json file. Only use this if you are having an issue with this field.",
"default": false
}
},
"required": ["tsConfig", "project", "main", "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

@ -0,0 +1,57 @@
import { Tree, readJson, NxJsonConfiguration, updateJson } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { rollupInitGenerator } from './init';
describe('rollupInitGenerator', () => {
let tree: Tree;
beforeEach(async () => {
tree = createTreeWithEmptyWorkspace();
});
it('should support babel', async () => {
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.namedInputs = {
sharedGlobals: ['{workspaceRoot}/exiting-file.json'],
};
return json;
});
await rollupInitGenerator(tree, { compiler: 'babel' });
expect(tree.exists('babel.config.json'));
const sharedGlobals = readJson<NxJsonConfiguration>(tree, 'nx.json')
.namedInputs.sharedGlobals;
expect(sharedGlobals).toContain('{workspaceRoot}/exiting-file.json');
expect(sharedGlobals).toContain('{workspaceRoot}/babel.config.json');
});
it('should support swc', async () => {
await rollupInitGenerator(tree, { compiler: 'swc' });
const packageJson = readJson(tree, 'package.json');
expect(packageJson).toEqual({
name: expect.any(String),
dependencies: {},
devDependencies: {
'@swc/helpers': expect.any(String),
'@swc/core': expect.any(String),
'swc-loader': expect.any(String),
},
});
});
it('should support tsc', async () => {
await rollupInitGenerator(tree, { compiler: 'tsc' });
const packageJson = readJson(tree, 'package.json');
expect(packageJson).toEqual({
name: expect.any(String),
dependencies: {},
devDependencies: {
tslib: expect.any(String),
},
});
});
});

View File

@ -0,0 +1,66 @@
import {
addDependenciesToPackageJson,
convertNxGenerator,
formatFiles,
GeneratorCallback,
readWorkspaceConfiguration,
Tree,
updateWorkspaceConfiguration,
writeJson,
} from '@nrwl/devkit';
import { Schema } from './schema';
import { swcCoreVersion, swcHelpersVersion } from '@nrwl/js/src/utils/versions';
import { swcLoaderVersion, tsLibVersion } from '../../utils/versions';
export async function rollupInitGenerator(tree: Tree, schema: Schema) {
let task: GeneratorCallback;
if (schema.compiler === 'babel') {
initRootBabelConfig(tree);
}
if (schema.compiler === 'swc') {
task = addDependenciesToPackageJson(
tree,
{},
{
'@swc/helpers': swcHelpersVersion,
'@swc/core': swcCoreVersion,
'swc-loader': swcLoaderVersion,
}
);
}
if (schema.compiler === 'tsc') {
task = addDependenciesToPackageJson(tree, {}, { tslib: tsLibVersion });
}
if (!schema.skipFormat) {
await formatFiles(tree);
}
return task;
}
function initRootBabelConfig(tree: Tree) {
if (tree.exists('/babel.config.json') || tree.exists('/babel.config.js')) {
return;
}
writeJson(tree, '/babel.config.json', {
babelrcRoots: ['*'], // Make sure .babelrc files other than root can be loaded in a monorepo
});
const workspaceConfiguration = readWorkspaceConfiguration(tree);
if (workspaceConfiguration.namedInputs?.sharedGlobals) {
workspaceConfiguration.namedInputs.sharedGlobals.push(
'{workspaceRoot}/babel.config.json'
);
}
updateWorkspaceConfiguration(tree, workspaceConfiguration);
}
export default rollupInitGenerator;
export const rollupInitSchematic = convertNxGenerator(rollupInitGenerator);

View File

@ -0,0 +1,4 @@
export interface Schema {
compiler?: 'babel' | 'swc' | 'tsc';
skipFormat?: boolean;
}

View File

@ -0,0 +1,22 @@
{
"$schema": "http://json-schema.org/schema",
"$id": "NxWebpackInit",
"cli": "nx",
"title": "Init Webpack Plugin",
"description": "Init Webpack Plugin.",
"type": "object",
"properties": {
"compiler": {
"type": "string",
"enum": ["babel", "swc", "tsc"],
"description": "The compiler to initialize for.",
"default": "babel"
},
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
"default": false
}
},
"required": []
}

View File

@ -0,0 +1,102 @@
import {
addProjectConfiguration,
readJson,
readProjectConfiguration,
Tree,
writeJson,
} from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { rollupProjectGenerator } from './rollup-project';
describe('rollupProjectGenerator', () => {
let tree: Tree;
beforeEach(async () => {
tree = createTreeWithEmptyWorkspace();
addProjectConfiguration(tree, 'mypkg', {
root: 'libs/mypkg',
sourceRoot: 'libs/mypkg/src',
targets: {},
});
});
it('should generate files', async () => {
await rollupProjectGenerator(tree, {
project: 'mypkg',
});
const project = readProjectConfiguration(tree, 'mypkg');
expect(project.targets).toMatchObject({
build: {
executor: '@nrwl/rollup:rollup',
outputs: ['{options.outputPath}'],
defaultConfiguration: 'production',
options: {
main: 'libs/mypkg/src/main.ts',
},
},
});
expect(readJson(tree, 'libs/mypkg/package.json')).toEqual({
name: '@proj/mypkg',
version: '0.0.1',
});
});
it('should respect existing package.json file', async () => {
writeJson(tree, 'libs/mypkg/package.json', {
name: '@acme/mypkg',
version: '1.0.0',
});
await rollupProjectGenerator(tree, {
project: 'mypkg',
});
expect(readJson(tree, 'libs/mypkg/package.json')).toEqual({
name: '@acme/mypkg',
version: '1.0.0',
});
});
it('should support --main option', async () => {
await rollupProjectGenerator(tree, {
project: 'mypkg',
main: 'libs/mypkg/index.ts',
});
const project = readProjectConfiguration(tree, 'mypkg');
expect(project.targets).toMatchObject({
build: {
executor: '@nrwl/rollup:rollup',
outputs: ['{options.outputPath}'],
defaultConfiguration: 'production',
options: {
main: 'libs/mypkg/index.ts',
},
},
});
});
it('should support --tsConfig option', async () => {
await rollupProjectGenerator(tree, {
project: 'mypkg',
tsConfig: 'libs/mypkg/tsconfig.custom.json',
});
const project = readProjectConfiguration(tree, 'mypkg');
expect(project.targets).toMatchObject({
build: {
executor: '@nrwl/rollup:rollup',
outputs: ['{options.outputPath}'],
defaultConfiguration: 'production',
options: {
tsConfig: 'libs/mypkg/tsconfig.custom.json',
},
},
});
});
});

View File

@ -0,0 +1,105 @@
import type { Tree } from '@nrwl/devkit';
import {
convertNxGenerator,
formatFiles,
getImportPath,
getWorkspaceLayout,
joinPathFragments,
readProjectConfiguration,
updateProjectConfiguration,
writeJson,
} from '@nrwl/devkit';
import { rollupInitGenerator } from '../init/init';
import { RollupExecutorOptions } from '../../executors/rollup/schema';
import { RollupProjectSchema } from './schema';
export async function rollupProjectGenerator(
tree: Tree,
options: RollupProjectSchema
) {
const task = await rollupInitGenerator(tree, options);
checkForTargetConflicts(tree, options);
addBuildTarget(tree, options);
await formatFiles(tree);
return task;
}
function checkForTargetConflicts(tree: Tree, options: RollupProjectSchema) {
const project = readProjectConfiguration(tree, options.project);
if (project.targets.build) {
throw new Error(`Project "${project.name}" already has a build target.`);
}
if (options.devServer && project.targets.serve) {
throw new Error(`Project "${project.name}" already has a serve target.`);
}
}
function addBuildTarget(tree: Tree, options: RollupProjectSchema) {
const project = readProjectConfiguration(tree, options.project);
const packageJsonPath = joinPathFragments(project.root, 'package.json');
if (!tree.exists(packageJsonPath)) {
const { npmScope } = getWorkspaceLayout(tree);
const importPath =
options.importPath || getImportPath(npmScope, options.project);
writeJson(tree, packageJsonPath, {
name: importPath,
version: '0.0.1',
});
}
const tsConfig =
options.tsConfig ?? joinPathFragments(project.root, 'tsconfig.lib.json');
const buildOptions: RollupExecutorOptions = {
main: options.main ?? joinPathFragments(project.root, 'src/main.ts'),
outputPath: joinPathFragments('dist', project.root),
compiler: options.compiler ?? 'babel',
tsConfig,
project: `${project.root}/package.json`,
external: options.external,
};
if (options.rollupConfig) {
buildOptions.rollupConfig = options.rollupConfig;
}
if (tree.exists(joinPathFragments(project.root, 'README.md'))) {
buildOptions.assets = [
{
glob: `${project.root}/README.md`,
input: '.',
output: '.',
},
];
}
updateProjectConfiguration(tree, options.project, {
...project,
targets: {
...project.targets,
build: {
executor: '@nrwl/rollup:rollup',
outputs: ['{options.outputPath}'],
defaultConfiguration: 'production',
options: buildOptions,
configurations: {
production: {
optimization: true,
sourceMap: false,
namedChunks: false,
extractLicenses: true,
vendorChunk: false,
},
},
},
},
});
}
export default rollupProjectGenerator;
export const rollupProjectSchematic = convertNxGenerator(
rollupProjectGenerator
);

View File

@ -0,0 +1,12 @@
export interface RollupProjectSchema {
project: string;
main?: string;
tsConfig?: string;
compiler?: 'babel' | 'swc' | 'tsc';
devServer?: boolean;
skipFormat?: boolean;
skipPackageJson?: boolean;
importPath?: string;
external?: string[];
rollupConfig?: string;
}

View File

@ -0,0 +1,61 @@
{
"$schema": "http://json-schema.org/schema",
"$id": "NxRollupProject",
"cli": "nx",
"title": "Add Rollup Configuration to a project",
"description": "Add Rollup Configuration to a project.",
"type": "object",
"properties": {
"project": {
"type": "string",
"description": "The name of the project.",
"$default": {
"$source": "argv",
"index": 0
},
"x-dropdown": "project",
"x-prompt": "What is the name of the project to set up a rollup for?"
},
"compiler": {
"type": "string",
"enum": ["babel", "swc", "tsc"],
"description": "The compiler to use to build source.",
"default": "babel"
},
"main": {
"type": "string",
"description": "Path relative to the workspace root for the main entry file. Defaults to '<projectRoot>/src/main.ts'.",
"alias": "entryFile"
},
"tsConfig": {
"type": "string",
"description": "Path relative to the workspace root for the tsconfig file to build with. Defaults to '<projectRoot>/tsconfig.app.json'."
},
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
"default": false
},
"skipPackageJson": {
"type": "boolean",
"default": false,
"description": "Do not add dependencies to `package.json`."
},
"importPath": {
"type": "string",
"description": "The library name used to import it, like `@myorg/my-awesome-lib`."
},
"external": {
"type": "array",
"description": "A list of external modules that will not be bundled (`react`, `react-dom`, etc.).",
"items": {
"type": "string"
}
},
"rollupConfig": {
"type": "string",
"description": "Path relative to workspace root to a custom rollup file that takes a config object and returns an updated config."
}
},
"required": []
}

View File

@ -0,0 +1,14 @@
import * as path from 'path';
import { removeSync } from 'fs-extra';
/**
* Delete an output directory, but error out if it's the root of the project.
*/
export function deleteOutputDir(root: string, outputPath: string) {
const resolvedOutputPath = path.resolve(root, outputPath);
if (resolvedOutputPath === root) {
throw new Error('Output path MUST not be project root directory!');
}
removeSync(resolvedOutputPath);
}

View File

@ -0,0 +1,3 @@
export const nxVersion = require('../../package.json').version;
export const swcLoaderVersion = '0.1.15';
export const tsLibVersion = '^2.3.0';

View File

@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs"
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}

View File

@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"declaration": true,
"types": []
},
"include": ["**/*.ts"],
"exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts"]
}

View File

@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"]
}

View File

@ -41,30 +41,16 @@
"@nrwl/jest": "file:../jest",
"@nrwl/js": "file:../js",
"@nrwl/linter": "file:../linter",
"@nrwl/rollup": "file:../rollup",
"@nrwl/webpack": "file:../webpack",
"@nrwl/workspace": "file:../workspace",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^20.0.0",
"@rollup/plugin-image": "^2.1.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.0.4",
"autoprefixer": "^10.4.9",
"babel-plugin-const-enum": "^1.0.1",
"babel-plugin-macros": "^2.8.0",
"babel-plugin-transform-async-to-promises": "^0.8.15",
"babel-plugin-transform-typescript-metadata": "^0.3.1",
"chalk": "4.1.0",
"chokidar": "^3.5.1",
"fs-extra": "^10.1.0",
"http-server": "14.1.0",
"ignore": "^5.0.4",
"postcss": "^8.4.14",
"rollup": "^2.56.2",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-postcss": "^4.0.1",
"rollup-plugin-typescript2": "^0.31.1",
"rxjs": "^6.5.4",
"tslib": "^2.3.0"
}
}

View File

@ -1,46 +0,0 @@
import { dirname } from 'path';
import { AssetGlobPattern } from '@nrwl/webpack';
import { normalizeAssets, normalizePluginPath } from '@nrwl/webpack';
import { WebRollupOptions } from '../schema';
export interface NormalizedWebRollupOptions extends WebRollupOptions {
entryRoot: string;
projectRoot: string;
assets: AssetGlobPattern[];
rollupConfig: string[];
}
export function normalizeWebRollupOptions(
options: WebRollupOptions,
root: string,
sourceRoot: string
): NormalizedWebRollupOptions {
const entryFile = `${root}/${options.entryFile}`;
const entryRoot = dirname(entryFile);
const project = `${root}/${options.project}`;
const projectRoot = dirname(project);
const outputPath = `${root}/${options.outputPath}`;
if (options.buildableProjectDepsInPackageJsonType == undefined) {
options.buildableProjectDepsInPackageJsonType = 'peerDependencies';
}
return {
...options,
// de-dupe formats
format: Array.from(new Set(options.format)),
rollupConfig: []
.concat(options.rollupConfig)
.filter(Boolean)
.map((p) => normalizePluginPath(p, root)),
assets: options.assets
? normalizeAssets(options.assets, root, sourceRoot)
: undefined,
entryFile,
entryRoot,
project,
projectRoot,
outputPath,
};
}

View File

@ -1,327 +1,21 @@
import * as rollup from 'rollup';
import * as peerDepsExternal from 'rollup-plugin-peer-deps-external';
import { getBabelInputPlugin } from '@rollup/plugin-babel';
import { join } from 'path';
import { from, Observable, of } from 'rxjs';
import { catchError, concatMap, last, scan, tap } from 'rxjs/operators';
import { eachValueFrom } from '@nrwl/devkit/src/utils/rxjs-for-await';
import * as autoprefixer from 'autoprefixer';
import type { ExecutorContext } from '@nrwl/devkit';
import { logger, names, readJsonFile } from '@nrwl/devkit';
/**
* This is here for backwards-compat.
* TODO(jack): remove in Nx 16.
*/
import {
calculateProjectDependencies,
computeCompilerOptionsPaths,
DependentBuildableProjectNode,
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
import resolve from '@rollup/plugin-node-resolve';
rollupExecutor as _rollupExecutor,
RollupExecutorOptions as WebRollupOptions,
} from '@nrwl/rollup';
import { AssetGlobPattern } from '@nrwl/webpack';
import { WebRollupOptions } from './schema';
import { runRollup } from './lib/run-rollup';
import {
NormalizedWebRollupOptions,
normalizeWebRollupOptions,
} from './lib/normalize';
import { analyze } from './lib/analyze-plugin';
import { deleteOutputDir } from '../../utils/fs';
import { swc } from './lib/swc-plugin';
import { validateTypes } from './lib/validate-types';
import { updatePackageJson } from './lib/update-package-json';
export { WebRollupOptions };
// These use require because the ES import isn't correct.
const commonjs = require('@rollup/plugin-commonjs');
const image = require('@rollup/plugin-image');
const json = require('@rollup/plugin-json');
const copy = require('rollup-plugin-copy');
const postcss = require('rollup-plugin-postcss');
const fileExtensions = ['.js', '.jsx', '.ts', '.tsx'];
export default async function* rollupExecutor(
rawOptions: WebRollupOptions,
context: ExecutorContext
) {
process.env.NODE_ENV ??= 'production';
const project = context.workspace.projects[context.projectName];
const sourceRoot = project.sourceRoot;
const { target, dependencies } = calculateProjectDependencies(
context.projectGraph,
context.root,
context.projectName,
context.targetName,
context.configurationName,
true
);
const options = normalizeWebRollupOptions(
rawOptions,
context.root,
sourceRoot
);
// TODO(jack): Remove UMD in Nx 15
if (options.format.includes('umd')) {
if (options.format.includes('cjs')) {
throw new Error(
'Cannot use both UMD and CJS. We recommend you use ESM or CJS.'
);
} else {
logger.warn('UMD format is deprecated and will be removed in Nx 15');
}
}
const packageJson = readJsonFile(options.project);
const npmDeps = (context.projectGraph.dependencies[context.projectName] ?? [])
.filter((d) => d.target.startsWith('npm:'))
.map((d) => d.target.slice(4));
const rollupOptions = createRollupOptions(
options,
dependencies,
context,
packageJson,
sourceRoot,
npmDeps
);
if (options.compiler === 'swc') {
try {
await validateTypes({
workspaceRoot: context.root,
projectRoot: options.projectRoot,
tsconfig: options.tsConfig,
});
} catch {
return { success: false };
}
}
if (options.watch) {
const watcher = rollup.watch(rollupOptions);
return yield* eachValueFrom(
new Observable<{ success: boolean }>((obs) => {
watcher.on('event', (data) => {
if (data.code === 'START') {
logger.info(`Bundling ${context.projectName}...`);
} else if (data.code === 'END') {
updatePackageJson(
options,
context,
target,
dependencies,
packageJson
);
logger.info('Bundle complete. Watching for file changes...');
obs.next({ success: true });
} else if (data.code === 'ERROR') {
logger.error(`Error during bundle: ${data.error.message}`);
obs.next({ success: false });
}
});
// Teardown logic. Close watcher when unsubscribed.
return () => watcher.close();
})
);
} else {
logger.info(`Bundling ${context.projectName}...`);
// Delete output path before bundling
if (options.deleteOutputPath) {
deleteOutputDir(context.root, options.outputPath);
}
const start = process.hrtime.bigint();
return from(rollupOptions)
.pipe(
concatMap((opts) =>
runRollup(opts).pipe(
catchError((e) => {
logger.error(`Error during bundle: ${e}`);
return of({ success: false });
})
)
),
scan(
(acc, result) => {
if (!acc.success) return acc;
return result;
export async function* rollupExecutor(options: any, context: any) {
return yield* _rollupExecutor(
{
...options,
main: options.main || options.entryFile,
},
{ success: true }
),
last(),
tap({
next: (result) => {
if (result.success) {
const end = process.hrtime.bigint();
const duration = `${(Number(end - start) / 1_000_000_000).toFixed(
2
)}s`;
updatePackageJson(
options,
context,
target,
dependencies,
packageJson
context
);
logger.info(`⚡ Done in ${duration}`);
} else {
logger.error(`Bundle failed: ${context.projectName}`);
}
},
})
)
.toPromise();
}
}
// -----------------------------------------------------------------------------
export function createRollupOptions(
options: NormalizedWebRollupOptions,
dependencies: DependentBuildableProjectNode[],
context: ExecutorContext,
packageJson: any,
sourceRoot: string,
npmDeps: string[]
): rollup.InputOptions[] {
const useBabel = options.compiler === 'babel';
const useSwc = options.compiler === 'swc';
return options.format.map((format, idx) => {
const plugins = [
copy({
targets: convertCopyAssetsToRollupOptions(
options.outputPath,
options.assets
),
}),
image(),
json(),
useBabel &&
require('rollup-plugin-typescript2')({
check: true,
tsconfig: options.tsConfig,
tsconfigOverride: {
compilerOptions: createCompilerOptions(options, dependencies),
},
}),
peerDepsExternal({
packageJsonPath: options.project,
}),
postcss({
inject: true,
extract: options.extractCss,
autoModules: true,
plugins: [autoprefixer],
use: {
less: {
javascriptEnabled: options.javascriptEnabled,
},
},
}),
resolve({
preferBuiltins: true,
extensions: fileExtensions,
}),
useSwc && swc(),
useBabel &&
getBabelInputPlugin({
// Let's `@nrwl/web/babel` preset know that we are packaging.
caller: {
// @ts-ignore
// Ignoring type checks for caller since we have custom attributes
isNxPackage: true,
// Always target esnext and let rollup handle cjs/umd
supportsStaticESM: true,
isModern: true,
},
cwd: join(context.root, sourceRoot),
rootMode: 'upward',
babelrc: true,
extensions: fileExtensions,
babelHelpers: 'bundled',
skipPreflightCheck: true, // pre-flight check may yield false positives and also slows down the build
exclude: /node_modules/,
plugins: [
format === 'esm'
? undefined
: require.resolve('babel-plugin-transform-async-to-promises'),
].filter(Boolean),
}),
commonjs(),
analyze(),
];
const globals = options.globals
? options.globals.reduce(
(acc, item) => {
acc[item.moduleId] = item.global;
return acc;
},
{ 'react/jsx-runtime': 'jsxRuntime' }
)
: { 'react/jsx-runtime': 'jsxRuntime' };
const externalPackages = dependencies
.map((d) => d.name)
.concat(options.external || [])
.concat(Object.keys(packageJson.dependencies || {}));
const rollupConfig = {
input: options.entryFile,
output: {
globals,
format,
dir: `${options.outputPath}`,
name: options.umdName || names(context.projectName).className,
entryFileNames: `[name].${format === 'esm' ? 'js' : 'cjs'}`,
chunkFileNames: `[name].${format === 'esm' ? 'js' : 'cjs'}`,
// umd doesn't support code-split bundles
inlineDynamicImports: format === 'umd',
},
external: (id) =>
externalPackages.some(
(name) => id === name || id.startsWith(`${name}/`)
) || npmDeps.some((name) => id === name || id.startsWith(`${name}/`)), // Could be a deep import
plugins,
};
return options.rollupConfig.reduce((currentConfig, plugin) => {
return require(plugin)(currentConfig, options);
}, rollupConfig);
});
}
function createCompilerOptions(options, dependencies) {
const compilerOptionPaths = computeCompilerOptionsPaths(
options.tsConfig,
dependencies
);
return {
rootDir: options.entryRoot,
allowJs: false,
declaration: true,
paths: compilerOptionPaths,
};
}
interface RollupCopyAssetOption {
src: string;
dest: string;
}
function convertCopyAssetsToRollupOptions(
outputPath: string,
assets: AssetGlobPattern[]
): RollupCopyAssetOption[] {
return assets
? assets.map((a) => ({
src: join(a.input, a.glob).replace(/\\/g, '/'),
dest: join(outputPath, a.output).replace(/\\/g, '/'),
}))
: undefined;
}
export default rollupExecutor;

View File

@ -1,12 +1,12 @@
import { dirname, join, relative, resolve } from 'path';
import { dirname, join, relative } from 'path';
import { directoryExists, fileExists } from './fileutils';
import type { ProjectGraph, ProjectGraphProjectNode } from '@nrwl/devkit';
import {
getOutputsForTargetAndConfiguration,
ProjectGraphExternalNode,
readJsonFile,
stripIndents,
writeJsonFile,
getOutputsForTargetAndConfiguration,
} from '@nrwl/devkit';
import * as ts from 'typescript';
import { unlinkSync } from 'fs';
@ -163,8 +163,14 @@ function readTsConfigWithRemappedPaths(
return generatedTsConfig;
}
/**
* Util function to create tsconfig compilerOptions object with support for workspace libs paths.
*
* @param tsConfig String of config path or object parsed via ts.parseJsonConfigFileContent.
* @param dependencies Dependencies calculated by Nx.
*/
export function computeCompilerOptionsPaths(
tsConfig: string,
tsConfig: string | ts.ParsedCommandLine,
dependencies: DependentBuildableProjectNode[]
) {
const paths = readPaths(tsConfig) || {};
@ -172,16 +178,21 @@ export function computeCompilerOptionsPaths(
return paths;
}
function readPaths(tsConfig: string) {
function readPaths(tsConfig: string | ts.ParsedCommandLine) {
try {
const parsedTSConfig = ts.readConfigFile(tsConfig, ts.sys.readFile).config;
if (
parsedTSConfig.compilerOptions &&
parsedTSConfig.compilerOptions.paths
) {
return parsedTSConfig.compilerOptions.paths;
} else if (parsedTSConfig.extends) {
return readPaths(resolve(dirname(tsConfig), parsedTSConfig.extends));
let config: ts.ParsedCommandLine;
if (typeof tsConfig === 'string') {
const configFile = ts.readConfigFile(tsConfig, ts.sys.readFile);
config = ts.parseJsonConfigFileContent(
configFile.config,
ts.sys,
dirname(tsConfig)
);
} else {
config = tsConfig;
}
if (config.options?.paths) {
return config.options.paths;
} else {
return null;
}

View File

@ -86,6 +86,7 @@ const IGNORE_MATCHES = {
'swc-loader',
'tsconfig-paths-webpack-plugin',
],
rollup: ['@swc/core'],
storybook: [
'@angular-devkit/architect',
'@angular-devkit/core',

View File

@ -19,6 +19,7 @@ const IGNORE_MATCHES = {
node: [],
'nx-plugin': [],
react: [],
rollup: [],
storybook: [],
nx: ['glob'],
web: ['http-server'],

View File

@ -83,6 +83,8 @@
"@nrwl/react": ["packages/react"],
"@nrwl/react-native": ["packages/react-native"],
"@nrwl/react/*": ["packages/react/*"],
"@nrwl/rollup": ["packages/rollup"],
"@nrwl/rollup/*": ["packages/rollup/*"],
"@nrwl/storybook": ["packages/storybook"],
"@nrwl/storybook/*": ["packages/storybook/*"],
"@nrwl/tao": ["packages/tao"],

View File

@ -29,6 +29,7 @@
"e2e-nx-run": "e2e/nx-run",
"e2e-react": "e2e/react",
"e2e-react-native": "e2e/react-native",
"e2e-rollup": "e2e/rollup",
"e2e-storybook": "e2e/storybook",
"e2e-storybook-angular": "e2e/storybook-angular",
"e2e-utils": "e2e/utils",
@ -71,6 +72,7 @@
"nx-plugin": "packages/nx-plugin",
"react": "packages/react",
"react-native": "packages/react-native",
"rollup": "packages/rollup",
"storybook": "packages/storybook",
"tao": "packages/tao",
"typedoc-theme": "typedoc-theme",