├── .gitignore ├── .npmrc ├── packages ├── rollup-config-pectin │ ├── .babelrc │ ├── tsconfig.json │ ├── lib │ │ └── rollup-config-pectin.ts │ ├── README.md │ ├── package.json │ ├── test │ │ └── rollup-config-pectin.test.ts │ └── CHANGELOG.md ├── pectin-api │ ├── auto.js │ ├── lib │ │ ├── pectin-api.ts │ │ ├── find-configs.ts │ │ ├── generate-config.ts │ │ └── is-up-to-date.ts │ ├── tsconfig.json │ ├── README.md │ ├── package.json │ ├── CHANGELOG.md │ └── test │ │ └── pectin-api.test.ts ├── pectin-babelrc │ ├── tsconfig.json │ ├── README.md │ ├── package.json │ ├── CHANGELOG.md │ ├── lib │ │ └── pectin-babelrc.ts │ └── test │ │ └── pectin-babelrc.test.ts ├── rollup-plugin-main-entry │ ├── tsconfig.json │ ├── README.md │ ├── package.json │ ├── CHANGELOG.md │ ├── lib │ │ └── rollup-plugin-main-entry.ts │ └── test │ │ └── rollup-plugin-main-entry.test.ts ├── rollup-plugin-subpath-externals │ ├── tsconfig.json │ ├── package.json │ ├── README.md │ ├── CHANGELOG.md │ ├── lib │ │ └── rollup-plugin-subpath-externals.ts │ └── test │ │ └── rollup-plugin-subpath-externals.test.ts ├── pectin │ ├── bin │ │ └── pectin │ ├── tsconfig.json │ ├── README.md │ ├── package.json │ ├── lib │ │ ├── invoke-rollup.ts │ │ └── pectin-cli.ts │ ├── test │ │ └── pectin-cli.test.ts │ └── CHANGELOG.md └── pectin-core │ ├── tsconfig.json │ ├── README.md │ ├── lib │ ├── getInput.ts │ ├── pectin-core.ts │ ├── getPlugins.ts │ └── getOutput.ts │ ├── package.json │ ├── CHANGELOG.md │ └── test │ └── pectin-core.test.ts ├── .editorconfig ├── lerna.json ├── .travis.yml ├── .prettierrc.js ├── jest.config.js ├── tsconfig.json ├── LICENSE ├── tsconfig-base.json ├── toMatchTacks.js ├── .eslintrc.yaml ├── package.json ├── CODE_OF_CONDUCT.md ├── README.md └── CHANGELOG.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | dist 4 | *.log 5 | .vscode 6 | *.tsbuildinfo 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # just in case a private registry is configured in ~/.npmrc 2 | registry = https://registry.npmjs.org/ 3 | -------------------------------------------------------------------------------- /packages/rollup-config-pectin/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/pectin-api/auto.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./dist/pectin-api').findConfigs({ 4 | cwd: process.cwd(), 5 | }); 6 | -------------------------------------------------------------------------------- /packages/pectin-api/lib/pectin-api.ts: -------------------------------------------------------------------------------- 1 | export { findConfigs } from './find-configs'; 2 | export { generateConfig } from './generate-config'; 3 | export { isUpToDate } from './is-up-to-date'; 4 | -------------------------------------------------------------------------------- /packages/pectin-babelrc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig-base.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./lib" 6 | }, 7 | "include": [ 8 | "lib/**/*" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/rollup-plugin-main-entry/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig-base.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./lib" 6 | }, 7 | "include": [ 8 | "lib/**/*" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/rollup-plugin-subpath-externals/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig-base.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./lib" 6 | }, 7 | "include": [ 8 | "lib/**/*" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/pectin/bin/pectin: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | /* eslint-disable 6 | no-unused-expressions, 7 | node/no-missing-require, 8 | zillow/import/no-unresolved 9 | */ 10 | require('../dist/pectin-cli') 11 | .default() 12 | .parse(process.argv.slice(2)); 13 | -------------------------------------------------------------------------------- /packages/pectin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig-base.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./lib" 6 | }, 7 | "include": [ 8 | "lib/**/*" 9 | ], 10 | "references": [ 11 | { 12 | "path": "../pectin-api" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/pectin-api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig-base.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./lib" 6 | }, 7 | "include": [ 8 | "lib/**/*" 9 | ], 10 | "references": [ 11 | { 12 | "path": "../pectin-core" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/rollup-config-pectin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig-base.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./lib" 6 | }, 7 | "include": [ 8 | "lib/**/*" 9 | ], 10 | "references": [ 11 | { 12 | "path": "../pectin-core" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [{package.json,package-lock.json,lerna.json}] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [{*.yml,*.yaml}] 16 | indent_style = space 17 | indent_size = 2 18 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "command": { 3 | "publish": { 4 | "allowBranch": "latest", 5 | "conventionalCommits": true, 6 | "message": "chore(release): Publish packages" 7 | } 8 | }, 9 | "ignoreChanges": [ 10 | "**/__tests__/**", 11 | "**/test/**", 12 | "**/*.test.js", 13 | "**/*.md" 14 | ], 15 | "packages": [ 16 | "packages/*" 17 | ], 18 | "version": "independent" 19 | } 20 | -------------------------------------------------------------------------------- /packages/pectin-api/README.md: -------------------------------------------------------------------------------- 1 | # `@pectin/api` 2 | 3 | > Conventional rollup builds for lerna monorepos 4 | 5 | ## Usage 6 | 7 | ```js 8 | // rollup.config.js 9 | import { findConfigs } from '@pectin/api'; 10 | 11 | export default findConfigs(); 12 | ``` 13 | 14 | **Note:** This package requires node >=8.9. 15 | 16 | ## Related 17 | 18 | Check the [Pectin project docs](https://github.com/evocateur/pectin#readme) for more information. 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # don't build tags, it's redundant 2 | if: tag IS blank 3 | language: node_js 4 | node_js: 5 | - '12' 6 | - '10' 7 | - '8' 8 | cache: 9 | directories: 10 | - $HOME/.npm 11 | before_install: 12 | - nvm install-latest-npm 13 | install: 14 | - npm ci 15 | script: 16 | - npm test 17 | sudo: false 18 | env: 19 | global: 20 | - NO_UPDATE_NOTIFIER=1 21 | - HUSKY_SKIP_INSTALL=1 22 | matrix: 23 | fast_finish: true 24 | -------------------------------------------------------------------------------- /packages/pectin-core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig-base.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./lib" 6 | }, 7 | "include": [ 8 | "lib/**/*" 9 | ], 10 | "references": [ 11 | { 12 | "path": "../pectin-babelrc" 13 | }, 14 | { 15 | "path": "../rollup-plugin-main-entry" 16 | }, 17 | { 18 | "path": "../rollup-plugin-subpath-externals" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /packages/rollup-config-pectin/lib/rollup-config-pectin.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import pectin from '@pectin/core'; 3 | 4 | import { CoreProperties as PackageManifest } from '@schemastore/package'; 5 | 6 | const cwd = process.cwd(); 7 | // eslint-disable-next-line zillow/import/no-dynamic-require, @typescript-eslint/no-var-requires 8 | const pkg: PackageManifest = require(path.resolve(cwd, 'package.json')); 9 | 10 | // this needs to stay a raw CJS default export 11 | module.exports = pectin(pkg, { cwd }); 12 | -------------------------------------------------------------------------------- /packages/rollup-config-pectin/README.md: -------------------------------------------------------------------------------- 1 | # `rollup-config-pectin` 2 | 3 | > Rollup config for tree-shakeable libraries based on conventional patterns 4 | 5 | ## Usage 6 | 7 | ```sh 8 | rollup -c node:pectin -i src/index.js 9 | ``` 10 | 11 | Your `package.json` needs _both_ a `main` and `module` property. 12 | The preset used in your `.babelrc` file _must_ accept options. 13 | 14 | **Note:** This package requires node >=8.9. 15 | 16 | ## Related 17 | 18 | Check the [Pectin project docs](https://github.com/evocateur/pectin#readme) for more information. 19 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const config = require('prettier-config-zillow'); 4 | 5 | // just here so the editor plugins don't get fired 6 | module.exports = Object.assign({}, config, { 7 | overrides: [ 8 | { 9 | files: [ 10 | 'lerna.json', 11 | 'package.json', 12 | 'package-lock.json', 13 | '.eslintrc.yaml', 14 | '.travis.yml', 15 | ], 16 | options: { 17 | tabWidth: 2, 18 | }, 19 | }, 20 | ], 21 | }); 22 | -------------------------------------------------------------------------------- /packages/pectin-babelrc/README.md: -------------------------------------------------------------------------------- 1 | # `@pectin/babelrc` 2 | 3 | > Locate and prepare custom babel config for rollup-plugin-babel 4 | 5 | ## Usage 6 | 7 | In a `rollup.config.js`: 8 | 9 | ``` 10 | const babel = require('rollup-plugin-babel'); 11 | const babelrc = require('@pectin/babelrc'); 12 | const pkg = require('./package.json); 13 | 14 | module.exports = { 15 | plugins: [ 16 | babel(babelrc(pkg)) 17 | ] 18 | }; 19 | ``` 20 | 21 | **Note:** This package requires node >=8.9. 22 | 23 | ## Related 24 | 25 | Check the [Pectin project docs](https://github.com/evocateur/pectin#readme) for more information. 26 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // http://facebook.github.io/jest/docs/en/configuration.html#content 4 | module.exports = { 5 | cacheDirectory: '/node_modules/.cache/jest', 6 | clearMocks: true, 7 | collectCoverageFrom: ['**/lib/*.ts'], 8 | coverageDirectory: '/coverage', 9 | coverageReporters: ['cobertura', 'html', 'text'], 10 | coverageThreshold: { 11 | global: { 12 | branches: 100, 13 | functions: 100, 14 | lines: 100, 15 | statements: 100, 16 | }, 17 | }, 18 | roots: ['/packages'], 19 | testEnvironment: 'node', 20 | preset: 'ts-jest', 21 | }; 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a "solution" file that points to all of the leaves, as suggested at 3 | * https://www.typescriptlang.org/docs/handbook/project-references.html 4 | */ 5 | { 6 | "references": [ 7 | { "path": "packages/pectin" }, 8 | { "path": "packages/pectin-api" }, 9 | { "path": "packages/pectin-babelrc" }, 10 | { "path": "packages/pectin-core" }, 11 | { "path": "packages/rollup-config-pectin" }, 12 | { "path": "packages/rollup-plugin-main-entry" }, 13 | { "path": "packages/rollup-plugin-subpath-externals" } 14 | ], 15 | /* The remaining fields are necessary to prevent double compilation */ 16 | "files": [], 17 | "include": [] 18 | } 19 | -------------------------------------------------------------------------------- /packages/pectin/README.md: -------------------------------------------------------------------------------- 1 | # `pectin` 2 | 3 | > Lightweight CLI for running convention-based rollup builds in lerna monorepos 4 | 5 | ## Installation 6 | 7 | Add it to your local build by running the following command: 8 | 9 | ```sh 10 | npm i -D pectin 11 | ``` 12 | 13 | ## Usage 14 | 15 | Test locally with `npx`: 16 | 17 | ```sh 18 | npx pectin 19 | ``` 20 | 21 | Pass `--help` to describe available options. 22 | 23 | Calling it from an npm script is convenient for CI: 24 | 25 | ```json 26 | { 27 | "scripts": { 28 | "build": "pectin" 29 | } 30 | } 31 | ``` 32 | 33 | ```sh 34 | npm run build 35 | ``` 36 | 37 | ## Related 38 | 39 | Check the [Pectin project docs](https://github.com/evocateur/pectin#readme) for more information. 40 | -------------------------------------------------------------------------------- /packages/rollup-plugin-main-entry/README.md: -------------------------------------------------------------------------------- 1 | # `rollup-plugin-main-entry` 2 | 3 | > Conventional entry point for rollup builds with pkg.main 4 | 5 | ## Usage 6 | 7 | Use this plugin in your `rollup.config.js` to default the entry point using the `main` property of your `package.json` file. 8 | 9 | ``` 10 | const mainEntry = require('rollup-plugin-main-entry'); 11 | const pkg = require('./package.json); 12 | 13 | module.exports = { 14 | plugins: [ 15 | mainEntry(pkg) 16 | ] 17 | }; 18 | ``` 19 | 20 | If `pkg.main` is `"lib/index.js"`, the rollup entry will be `src/index.js`. 21 | 22 | **Note:** This package requires node >=8.9. 23 | 24 | ## Related 25 | 26 | Check the [Pectin project docs](https://github.com/evocateur/pectin#readme) for more information. 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2018, Daniel Stockman 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /packages/rollup-plugin-main-entry/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollup-plugin-main-entry", 3 | "version": "3.2.0", 4 | "description": "Conventional entry point for rollup builds with pkg.main", 5 | "keywords": [ 6 | "rollup", 7 | "plugin", 8 | "main", 9 | "entry" 10 | ], 11 | "author": "Daniel Stockman ", 12 | "homepage": "https://github.com/evocateur/pectin/tree/latest/packages/rollup-plugin-main-entry#readme", 13 | "license": "ISC", 14 | "main": "dist/rollup-plugin-main-entry.js", 15 | "types": "dist/rollup-plugin-main-entry.d.ts", 16 | "files": [ 17 | "dist" 18 | ], 19 | "engines": { 20 | "node": ">=8.9" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/evocateur/pectin.git" 25 | }, 26 | "scripts": { 27 | "test": "echo \"Error: run tests from root\" && exit 1" 28 | }, 29 | "peerDependencies": { 30 | "rollup": ">=1.0.0" 31 | }, 32 | "dependencies": { 33 | "@schemastore/package": "^0.0.5" 34 | }, 35 | "devDependencies": { 36 | "ts-jest": "^24.2.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/pectin-core/README.md: -------------------------------------------------------------------------------- 1 | # `@pectin/core` 2 | 3 | > Core implementation of pectin logic 4 | 5 | ## Usage 6 | 7 | ```js 8 | 'use strict'; 9 | 10 | const pectin = require('@pectin/core'); 11 | 12 | const cwd = process.cwd(); // default 13 | const pkg = require('./package.json'); 14 | 15 | const rollupConfig = pectin(pkg, { cwd }); 16 | ``` 17 | 18 | The generated Rollup config is used in both `rollup-config-pectin` and `@pectin/api`. 19 | 20 | **Note:** This package requires node >=8.9. 21 | 22 | ## Options 23 | 24 | To transform SVG imports into inlined data URIs, pass `rollup.inlineSVG = true` in your package.json: 25 | 26 | ```json 27 | { 28 | "name": "my-pkg", 29 | "version": "1.0.0", 30 | "rollup": { 31 | "inlineSVG": true 32 | } 33 | } 34 | ``` 35 | 36 | If you are using [babel-plugin-inline-react-svg](https://github.com/airbnb/babel-plugin-inline-react-svg) in your Babel config you **should not** configure this option as it will break the plugin. 37 | 38 | ## Related 39 | 40 | Check the [Pectin project docs](https://github.com/evocateur/pectin#readme) for more information. 41 | -------------------------------------------------------------------------------- /packages/rollup-config-pectin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollup-config-pectin", 3 | "version": "4.0.3", 4 | "description": "Rollup config for tree-shakeable libraries based on conventional patterns", 5 | "keywords": [ 6 | "rollup", 7 | "config", 8 | "tree-shake", 9 | "module" 10 | ], 11 | "author": "Daniel Stockman ", 12 | "homepage": "https://github.com/evocateur/pectin/tree/latest/packages/rollup-config-pectin#readme", 13 | "license": "ISC", 14 | "main": "dist/rollup-config-pectin.js", 15 | "types": "dist/rollup-config-pectin.d.ts", 16 | "files": [ 17 | "dist" 18 | ], 19 | "engines": { 20 | "node": ">=8.9" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/evocateur/pectin.git" 25 | }, 26 | "scripts": { 27 | "test": "echo \"Error: run tests from root\" && exit 1" 28 | }, 29 | "peerDependencies": { 30 | "rollup": ">=1.12.0" 31 | }, 32 | "dependencies": { 33 | "@pectin/core": "file:../pectin-core", 34 | "@schemastore/package": "^0.0.5" 35 | }, 36 | "devDependencies": { 37 | "ts-jest": "^24.2.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/rollup-plugin-main-entry/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [3.2.0](https://github.com/evocateur/pectin/compare/v3.1.1...v3.2.0) (2019-05-03) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **main-entry:** Adapt to rollup v1.11.0 change to options.input default value ([232b9a8](https://github.com/evocateur/pectin/commit/232b9a8)) 12 | 13 | 14 | 15 | 16 | 17 | # [3.0.0](https://github.com/evocateur/pectin/compare/v2.6.0...v3.0.0) (2019-01-01) 18 | 19 | 20 | ### Features 21 | 22 | * Rollup 1.0 🎉 ([938db4e](https://github.com/evocateur/pectin/commit/938db4e)) 23 | 24 | 25 | ### BREAKING CHANGES 26 | 27 | * Rollup 0.x is no longer supported. 28 | 29 | 30 | 31 | 32 | 33 | # [2.0.0](https://github.com/evocateur/pectin/compare/v1.3.0...v2.0.0) (2018-10-10) 34 | 35 | **Note:** Version bump only for package rollup-plugin-main-entry 36 | 37 | 38 | 39 | 40 | 41 | 42 | # 1.0.0 (2018-08-31) 43 | 44 | 45 | ### Features 46 | 47 | * open source ([ce2d5cb](https://github.com/evocateur/pectin/commit/ce2d5cb)) 48 | -------------------------------------------------------------------------------- /packages/rollup-plugin-subpath-externals/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollup-plugin-subpath-externals", 3 | "version": "3.4.0", 4 | "description": "Externalize all dependencies, even subpath imports", 5 | "keywords": [ 6 | "rollup", 7 | "plugin", 8 | "subpath", 9 | "externals" 10 | ], 11 | "author": "Daniel Stockman ", 12 | "homepage": "https://github.com/evocateur/pectin/tree/latest/packages/rollup-plugin-subpath-externals#readme", 13 | "license": "ISC", 14 | "main": "dist/rollup-plugin-subpath-externals.js", 15 | "types": "dist/rollup-plugin-subpath-externals.d.ts", 16 | "files": [ 17 | "dist" 18 | ], 19 | "engines": { 20 | "node": ">=8.9" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/evocateur/pectin.git" 25 | }, 26 | "scripts": { 27 | "test": "echo \"Error: run tests from root\" && exit 1" 28 | }, 29 | "peerDependencies": { 30 | "rollup": ">=1.0.0" 31 | }, 32 | "dependencies": { 33 | "@schemastore/package": "^0.0.5", 34 | "builtin-modules": "^3.0.0", 35 | "dot-prop": "^5.1.0" 36 | }, 37 | "devDependencies": { 38 | "ts-jest": "^24.2.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/pectin-core/lib/getInput.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import dotProp = require('dot-prop'); 3 | 4 | import { CoreProperties as PackageManifest } from '@schemastore/package'; 5 | 6 | function rebaseInput(rootDir: string, filePath: string): string { 7 | return path.join(rootDir, path.basename(filePath)); 8 | } 9 | 10 | /** 11 | * By convention, entry points live in the 'src' directory with 12 | * the same filename as pkg.main. 13 | * 14 | * This can be customized by optional properties in pkg.rollup: 15 | * - pkg.rollup.rootDir: changes the value of 'src' 16 | * - pkg.rollup.input: the full path to entry file 17 | * 18 | * @param {PackageManifest} pkg 19 | * @return {String} input path resolved to cwd 20 | */ 21 | export function getInput(pkg: PackageManifest, cwd: string): string { 22 | if (!pkg.main) { 23 | const location = path.relative('.', path.join(cwd, 'package.json')); 24 | 25 | throw new TypeError(`required field 'main' missing in ${location}`); 26 | } 27 | 28 | const rootDir = dotProp.get(pkg, 'rollup.rootDir', 'src'); 29 | const input = dotProp.get(pkg, 'rollup.input', rebaseInput(rootDir, pkg.main)); 30 | 31 | return path.resolve(cwd, input); 32 | } 33 | -------------------------------------------------------------------------------- /packages/pectin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pectin", 3 | "version": "3.6.1", 4 | "description": "Lightweight CLI for running convention-based rollup builds in lerna monorepos", 5 | "keywords": [ 6 | "rollup", 7 | "cli", 8 | "lerna", 9 | "monorepo" 10 | ], 11 | "author": "Daniel Stockman ", 12 | "homepage": "https://github.com/evocateur/pectin/tree/latest/packages/pectin#readme", 13 | "license": "ISC", 14 | "main": "dist/pectin-cli.js", 15 | "types": "dist/pectin-cli.d.ts", 16 | "bin": { 17 | "pectin": "bin/pectin" 18 | }, 19 | "files": [ 20 | "bin", 21 | "dist" 22 | ], 23 | "engines": { 24 | "node": ">=8.9" 25 | }, 26 | "publishConfig": { 27 | "access": "public" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/evocateur/pectin.git" 32 | }, 33 | "scripts": { 34 | "test": "echo \"Error: run tests from root\" && exit 1" 35 | }, 36 | "dependencies": { 37 | "@pectin/api": "file:../pectin-api", 38 | "npmlog": "^4.1.2", 39 | "resolve-from": "^5.0.0", 40 | "rollup": "^1.23.1", 41 | "yargs": "^14.2.0" 42 | }, 43 | "devDependencies": { 44 | "ts-jest": "^24.2.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tsconfig-base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "es2017", 5 | "module": "commonjs", 6 | "lib": ["es2017"], 7 | "declaration": true, 8 | "declarationMap": true, 9 | "sourceMap": true, 10 | "composite": true, 11 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 12 | 13 | /* Strict Type-Checking Options */ 14 | "strict": true, 15 | 16 | /* Additional Checks */ 17 | "noUnusedLocals": true, /* Report errors on unused locals. */ 18 | "noUnusedParameters": true, /* Report errors on unused parameters. */ 19 | "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 20 | "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 21 | 22 | /* Module Resolution Options */ 23 | "moduleResolution": "node", 24 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 25 | 26 | /* Advanced Options */ 27 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/pectin-core/lib/pectin-core.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import pMap = require('p-map'); 3 | import { getInput } from './getInput'; 4 | import { getOutput } from './getOutput'; 5 | import { getPlugins } from './getPlugins'; 6 | 7 | import { CoreProperties as PackageManifest } from '@schemastore/package'; 8 | import { RollupOptions } from 'rollup'; 9 | 10 | export { RollupOutputOptions } from './getOutput'; 11 | 12 | /** 13 | * Generate Rollup configs for a package. 14 | * 15 | * @param {PackageManifest} pkg parsed package.json 16 | * @param {Object} [opts] optional options object 17 | * @param {String} [opts.cwd] current working directory 18 | */ 19 | export default async function pectin( 20 | pkg: PackageManifest, 21 | opts?: { cwd?: string } 22 | ): Promise { 23 | const cwd = path.resolve((opts && opts.cwd) || '.'); 24 | const input = getInput(pkg, cwd); 25 | const outputs = getOutput(pkg, cwd); 26 | 27 | return pMap(outputs, async output => { 28 | const plugins = await getPlugins(pkg, cwd, output); 29 | 30 | return { 31 | input, 32 | output: [output], 33 | plugins, 34 | inlineDynamicImports: output.browser === true || output.format === 'umd', 35 | }; 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /packages/pectin/lib/invoke-rollup.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import resolveFrom = require('resolve-from'); 3 | 4 | // istanbul ignore next 5 | export function invokeRollup(argv: { 6 | concurrency: number; 7 | cwd: string; 8 | watch: boolean; 9 | _: string[]; 10 | }): void { 11 | const corePath = resolveFrom(__dirname, '@pectin/api/package.json'); 12 | const autoPath = path.join(path.dirname(corePath), 'auto.js'); 13 | const opts = ['--config', autoPath]; 14 | 15 | if (argv.watch) { 16 | opts.unshift('--watch'); 17 | } 18 | 19 | if (argv._.length) { 20 | opts.push(...argv._); 21 | } 22 | 23 | /* eslint-disable 24 | global-require, 25 | zillow/import/no-dynamic-require, 26 | @typescript-eslint/no-var-requires 27 | */ 28 | // @see https://github.com/zkat/npx/blob/b7c8b9f07605b9f41931ad3ef8e74a65d2f062bb/index.js#L258-L268 29 | const Module = require('module'); 30 | const rollupPkg = resolveFrom(argv.cwd, 'rollup/package.json'); 31 | // instead of hard-coded subpath, read package.json metadata to retrieve bin location 32 | const rollupBin = path.join(path.dirname(rollupPkg), require(rollupPkg).bin.rollup); 33 | 34 | process.argv = [process.argv[0], rollupBin].concat(opts); 35 | 36 | // ✨MAGIC✨ 37 | Module.runMain(); 38 | } 39 | -------------------------------------------------------------------------------- /packages/rollup-plugin-main-entry/lib/rollup-plugin-main-entry.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | 3 | import { CoreProperties as PackageManifest } from '@schemastore/package'; 4 | import { Plugin, InputOptions } from 'rollup'; 5 | 6 | export default function mainEntry(pkg: PackageManifest, cwd: string = process.cwd()): Plugin { 7 | const { rollup: { rootDir = 'src' } = {} } = pkg; 8 | 9 | if (!pkg.main) { 10 | const location = path.relative('.', path.join(cwd, 'package.json')); 11 | 12 | throw new TypeError(`required field 'main' missing in ${location}`); 13 | } 14 | 15 | return { 16 | name: 'main-entry', 17 | options: (opts): InputOptions => { 18 | // by convention, entry points always live in 'src' directory 19 | // with the same filename as pkg.main 20 | if ( 21 | !opts.input || 22 | // rollup v1.11.0 now defaults missing input to an empty array 23 | (Array.isArray(opts.input) && opts.input.length === 0) 24 | ) { 25 | // eslint-disable-next-line no-param-reassign, @typescript-eslint/no-non-null-assertion 26 | opts.input = [path.resolve(cwd, rootDir as string, path.basename(pkg.main!))]; 27 | } 28 | 29 | return opts; 30 | }, 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /packages/pectin-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@pectin/api", 3 | "version": "4.0.5", 4 | "description": "Conventional rollup builds for lerna monorepos", 5 | "keywords": [ 6 | "rollup", 7 | "build", 8 | "lerna", 9 | "monorepo" 10 | ], 11 | "author": "Daniel Stockman ", 12 | "homepage": "https://github.com/evocateur/pectin/tree/latest/packages/pectin-api#readme", 13 | "license": "ISC", 14 | "main": "dist/pectin-api.js", 15 | "types": "dist/pectin-api.d.ts", 16 | "files": [ 17 | "auto.js", 18 | "dist" 19 | ], 20 | "engines": { 21 | "node": ">=8.9" 22 | }, 23 | "publishConfig": { 24 | "access": "public" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git+https://github.com/evocateur/pectin.git" 29 | }, 30 | "scripts": { 31 | "test": "echo \"Error: run tests from root\" && exit 1" 32 | }, 33 | "peerDependencies": { 34 | "rollup": ">=1.12.0" 35 | }, 36 | "dependencies": { 37 | "@lerna/project": "^3.0.0", 38 | "@lerna/run-topologically": "^3.16.0", 39 | "@pectin/core": "file:../pectin-core", 40 | "@schemastore/package": "^0.0.5", 41 | "dot-prop": "^5.1.0", 42 | "globby": "^10.0.1" 43 | }, 44 | "devDependencies": { 45 | "tacks": "^1.3.0", 46 | "tempy": "^0.3.0", 47 | "touch": "^3.1.0", 48 | "ts-jest": "^24.2.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/pectin-babelrc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@pectin/babelrc", 3 | "version": "3.3.3", 4 | "description": "Locate and prepare custom babel config for rollup-plugin-babel", 5 | "keywords": [ 6 | "rollup", 7 | "babelrc" 8 | ], 9 | "author": "Daniel Stockman ", 10 | "homepage": "https://github.com/evocateur/pectin/tree/latest/packages/pectin-babelrc#readme", 11 | "license": "ISC", 12 | "main": "dist/pectin-babelrc.js", 13 | "types": "dist/pectin-babelrc.d.ts", 14 | "files": [ 15 | "dist" 16 | ], 17 | "engines": { 18 | "node": ">=8.9" 19 | }, 20 | "publishConfig": { 21 | "access": "public" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/evocateur/pectin.git" 26 | }, 27 | "scripts": { 28 | "test": "echo \"Error: run tests from root\" && exit 1" 29 | }, 30 | "peerDependencies": { 31 | "rollup": ">=1.12.0", 32 | "rollup-plugin-babel": "^4.2.0" 33 | }, 34 | "dependencies": { 35 | "@babel/plugin-syntax-dynamic-import": "^7.2.0", 36 | "@babel/plugin-transform-runtime": "^7.2.0", 37 | "@schemastore/package": "^0.0.5", 38 | "clone-deep": "^4.0.1", 39 | "cosmiconfig": "^5.2.0", 40 | "resolve-from": "^5.0.0" 41 | }, 42 | "devDependencies": { 43 | "@babel/runtime": "^7.7.4", 44 | "@babel/runtime-corejs2": "^7.7.4", 45 | "@babel/runtime-corejs3": "^7.7.4", 46 | "tacks": "^1.3.0", 47 | "tempy": "^0.3.0", 48 | "ts-jest": "^24.2.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/rollup-plugin-subpath-externals/README.md: -------------------------------------------------------------------------------- 1 | # `rollup-plugin-subpath-externals` 2 | 3 | > Externalize all dependencies, even subpath imports 4 | 5 | ## Usage 6 | 7 | Use this plugin in your `rollup.config.js` to externalize all `dependencies`, even subpath imports (lodash, babel-runtime) and `peerDependencies`. 8 | 9 | ```js 10 | const subpathExternals = require('rollup-plugin-subpath-externals'); 11 | const pkg = require('./package.json'); 12 | 13 | module.exports = { 14 | plugins: [ 15 | subpathExternals(pkg) 16 | ] 17 | }; 18 | ``` 19 | 20 | **Note:** This package requires node >=8.9. 21 | 22 | ## Options 23 | 24 | To express more fine-grained control over what dependencies are externalized, you may pass package props under the `rollup` namespace: 25 | 26 | ### Explicit External 27 | 28 | Only module names passed to `rollup.external` (_and_ builtin modules) will be externalized, all others will be inlined. 29 | 30 | ```json 31 | { 32 | "rollup": { 33 | "external": ["lodash"] 34 | } 35 | } 36 | ``` 37 | 38 | ### Partial Bundling 39 | 40 | Any dependency names passed to `rollup.bundle` will always be inlined, not externalized. 41 | 42 | ```json 43 | { 44 | "rollup": { 45 | "bundle": ["three"] 46 | } 47 | } 48 | ``` 49 | 50 | `rollup.bundle` is processed after `rollup.external`, and thus any duplicates between the two collections will always be inlined. 51 | 52 | ## Related 53 | 54 | Check the [Pectin project docs](https://github.com/evocateur/pectin#readme) for more information. 55 | -------------------------------------------------------------------------------- /packages/pectin-api/lib/find-configs.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/ban-ts-ignore */ 2 | import os = require('os'); 3 | // @ts-ignore pending migration to typescript 4 | import project = require('@lerna/project'); 5 | // @ts-ignore pending migration to typescript 6 | import runTopologically = require('@lerna/run-topologically'); 7 | /* eslint-enable */ 8 | 9 | import { CoreProperties as PackageManifest } from '@schemastore/package'; 10 | import { RollupOptions } from 'rollup'; 11 | 12 | import { generateConfig } from './generate-config'; 13 | 14 | export async function findConfigs(opts?: { 15 | cwd?: string; 16 | concurrency?: number; 17 | watch?: boolean; 18 | }): Promise { 19 | const concurrency = (opts && opts.concurrency) || os.cpus().length; 20 | const watch = 21 | opts && typeof opts.watch !== 'undefined' ? opts.watch : !!process.env.ROLLUP_WATCH; 22 | 23 | const lernaPackages = await project.getPackages((opts && opts.cwd) || process.cwd()); 24 | const configs = await runTopologically( 25 | lernaPackages, 26 | // clones internal JSON, maps synthetic location to cwd property 27 | (pkg: { toJSON: () => PackageManifest; location: string }) => 28 | generateConfig(pkg.toJSON(), { cwd: pkg.location, watch }), 29 | { concurrency } 30 | ); 31 | 32 | // flatten then compact 33 | return configs 34 | .reduce((acc: RollupOptions[], val: RollupOptions) => acc.concat(val), []) 35 | .filter((x: RollupOptions) => Boolean(x)); 36 | } 37 | -------------------------------------------------------------------------------- /packages/pectin-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@pectin/core", 3 | "version": "4.0.3", 4 | "description": "Core implementation of pectin logic", 5 | "keywords": [ 6 | "rollup", 7 | "pectin", 8 | "config", 9 | "tree-shake" 10 | ], 11 | "author": "Daniel Stockman ", 12 | "homepage": "https://github.com/evocateur/pectin/tree/latest/packages/pectin-core#readme", 13 | "license": "ISC", 14 | "main": "dist/pectin-core.js", 15 | "types": "dist/pectin-core.d.ts", 16 | "files": [ 17 | "dist" 18 | ], 19 | "engines": { 20 | "node": ">=8.9" 21 | }, 22 | "publishConfig": { 23 | "access": "public" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/evocateur/pectin.git" 28 | }, 29 | "scripts": { 30 | "test": "echo \"Error: run tests from root\" && exit 1" 31 | }, 32 | "peerDependencies": { 33 | "rollup": ">=1.12.0" 34 | }, 35 | "dependencies": { 36 | "@babel/core": "^7.0.0", 37 | "@pectin/babelrc": "file:../pectin-babelrc", 38 | "@schemastore/package": "^0.0.5", 39 | "babel-core": "^7.0.0-bridge.0", 40 | "camelcase": "^5.0.0", 41 | "dot-prop": "^5.1.0", 42 | "npm-package-arg": "^6.1.0", 43 | "p-map": "^3.0.0", 44 | "rollup-plugin-babel": "^4.3.2", 45 | "rollup-plugin-commonjs": "^10.0.1", 46 | "rollup-plugin-json": "^4.0.0", 47 | "rollup-plugin-main-entry": "file:../rollup-plugin-main-entry", 48 | "rollup-plugin-node-resolve": "^5.2.0", 49 | "rollup-plugin-replace": "^2.1.1", 50 | "rollup-plugin-subpath-externals": "file:../rollup-plugin-subpath-externals", 51 | "rollup-plugin-svg": "^1.0.1", 52 | "rollup-plugin-terser": "^5.0.0" 53 | }, 54 | "devDependencies": { 55 | "tacks": "^1.3.0", 56 | "tempy": "^0.3.0", 57 | "ts-jest": "^24.2.0" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /packages/rollup-config-pectin/test/rollup-config-pectin.test.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | 3 | const REPO_ROOT = path.resolve(__dirname, '../../..'); 4 | const CONFIG_FILE = path.resolve(__dirname, '../lib/rollup-config-pectin'); 5 | 6 | expect.addSnapshotSerializer({ 7 | test(val) { 8 | return typeof val === 'string' && val.indexOf(REPO_ROOT) > -1; 9 | }, 10 | // eslint-disable-next-line @typescript-eslint/ban-ts-ignore 11 | // @ts-ignore (the types are wrong, yet again) 12 | serialize(val: string, config, indentation: string, depth: number) { 13 | const str = val.replace(REPO_ROOT, ''); 14 | 15 | // top-level strings don't need quotes, but nested ones do (object properties, etc) 16 | return depth ? `"${str}"` : str; 17 | }, 18 | }); 19 | 20 | describe('rollup-config-pectin', () => { 21 | it('exports rollup config from cwd', async () => { 22 | // config file expects to operate in CWD 23 | process.chdir(path.resolve(__dirname, '..')); 24 | 25 | // eslint-disable-next-line global-require, zillow/import/no-dynamic-require 26 | const config = await require(CONFIG_FILE); 27 | 28 | expect(config).toHaveLength(1); 29 | expect(config[0]).toMatchInlineSnapshot( 30 | { 31 | plugins: expect.any(Array), 32 | }, 33 | ` 34 | Object { 35 | "inlineDynamicImports": false, 36 | "input": "/packages/rollup-config-pectin/src/rollup-config-pectin.js", 37 | "output": Array [ 38 | Object { 39 | "chunkFileNames": "[name]-[hash].[format].js", 40 | "dir": "/packages/rollup-config-pectin/dist", 41 | "entryFileNames": "rollup-config-pectin.js", 42 | "exports": "auto", 43 | "format": "cjs", 44 | }, 45 | ], 46 | "plugins": Any, 47 | } 48 | ` 49 | ); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /toMatchTacks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* eslint-disable 4 | zillow/import/no-extraneous-dependencies, 5 | node/no-extraneous-require, 6 | node/no-unpublished-require 7 | */ 8 | const path = require('path'); 9 | const Tacks = require('tacks'); 10 | const loadFromDir = require('tacks/load-from-dir'); 11 | const diff = require('jest-diff'); 12 | /* eslint-enable */ 13 | 14 | module.exports = toMatchTacks; 15 | 16 | /** 17 | * A matcher for Tacks trees. 18 | * 19 | * const toMatchTacks = require('./toMatchTacks'); 20 | * expect.extend({ toMatchTacks }); 21 | * 22 | * @param {String} actualDirectory 23 | * @param {Object} expectedConfig 24 | */ 25 | function toMatchTacks(actualDirectory, expectedConfig) { 26 | const { matcherHint, printExpected, printReceived } = this.utils; 27 | 28 | const actual = loadFromDir(path.resolve(actualDirectory)).toSource(); 29 | const expected = new Tacks(expectedConfig).toSource(); 30 | const pass = actual === expected; 31 | 32 | const message = pass 33 | ? () => 34 | `${matcherHint('.not.toMatchTacks')}\n\n` + 35 | 'Expected value to not be:\n' + 36 | ` ${printExpected(expected)}\n` + 37 | 'Received:\n' + 38 | ` ${printReceived(actual)}` 39 | : () => { 40 | const diffString = diff(expected, actual, { 41 | expand: this.expand, 42 | }); 43 | 44 | return `${`${matcherHint('.toMatchTacks')}\n\n` + 45 | 'Expected value to be:\n' + 46 | ` ${printExpected(expected)}\n` + 47 | 'Received:\n' + 48 | ` ${printReceived(actual)}`}${ 49 | diffString ? `\n\nDifference:\n\n${diffString}` : '' 50 | }`; 51 | }; 52 | 53 | return { 54 | actual, 55 | expected, 56 | message, 57 | pass, 58 | }; 59 | } 60 | -------------------------------------------------------------------------------- /packages/rollup-plugin-subpath-externals/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [3.4.0](https://github.com/evocateur/pectin/compare/v3.3.0...v3.4.0) (2019-08-01) 7 | 8 | 9 | ### Features 10 | 11 | * **deps:** Upgrade dependencies ([923f92f](https://github.com/evocateur/pectin/commit/923f92f)) 12 | 13 | 14 | 15 | 16 | 17 | # [3.0.0](https://github.com/evocateur/pectin/compare/v2.6.0...v3.0.0) (2019-01-01) 18 | 19 | 20 | ### Features 21 | 22 | * Rollup 1.0 🎉 ([938db4e](https://github.com/evocateur/pectin/commit/938db4e)) 23 | 24 | 25 | ### BREAKING CHANGES 26 | 27 | * Rollup 0.x is no longer supported. 28 | 29 | 30 | 31 | 32 | 33 | ## [2.4.1](https://github.com/evocateur/pectin/compare/v2.4.0...v2.4.1) (2018-11-15) 34 | 35 | **Note:** Version bump only for package rollup-plugin-subpath-externals 36 | 37 | 38 | 39 | 40 | 41 | # [2.4.0](https://github.com/evocateur/pectin/compare/v2.3.0...v2.4.0) (2018-11-14) 42 | 43 | 44 | ### Features 45 | 46 | * **subpath-externals:** Accept package props for fine-grained control ([b4f134b](https://github.com/evocateur/pectin/commit/b4f134b)) 47 | 48 | 49 | 50 | 51 | 52 | # [2.1.0](https://github.com/evocateur/pectin/compare/v2.0.0...v2.1.0) (2018-10-16) 53 | 54 | 55 | ### Features 56 | 57 | * **subpath-externals:** Accept optional format config that controls which types of dependencies are externalized ([446440d](https://github.com/evocateur/pectin/commit/446440d)) 58 | 59 | 60 | 61 | 62 | 63 | # [2.0.0](https://github.com/evocateur/pectin/compare/v1.3.0...v2.0.0) (2018-10-10) 64 | 65 | **Note:** Version bump only for package rollup-plugin-subpath-externals 66 | 67 | 68 | 69 | 70 | 71 | 72 | # 1.0.0 (2018-08-31) 73 | 74 | 75 | ### Features 76 | 77 | * open source ([ce2d5cb](https://github.com/evocateur/pectin/commit/ce2d5cb)) 78 | -------------------------------------------------------------------------------- /packages/pectin-api/lib/generate-config.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import dotProp = require('dot-prop'); 3 | import pectin from '@pectin/core'; 4 | 5 | import { CoreProperties as PackageManifest } from '@schemastore/package'; 6 | import { RollupOptions } from 'rollup'; 7 | 8 | import { isUpToDate } from './is-up-to-date'; 9 | 10 | export async function generateConfig( 11 | pkg: PackageManifest, 12 | opts: { 13 | cwd?: string; 14 | watch?: boolean; 15 | } 16 | ): Promise { 17 | let config: RollupOptions[]; 18 | 19 | // completely ignore packages that opt-out 20 | if (dotProp.has(pkg, 'rollup.skip')) { 21 | return null; 22 | } 23 | 24 | // allow per-package opt-out of watch 25 | if (opts.watch && dotProp.has(pkg, 'rollup.ignoreWatch')) { 26 | return null; 27 | } 28 | 29 | // back-compat for old property location 30 | if (pkg.cwd) { 31 | // eslint-disable-next-line no-param-reassign 32 | opts = Object.assign({}, opts, { 33 | cwd: pkg.cwd, 34 | }); 35 | } 36 | 37 | try { 38 | config = await pectin(pkg, opts); 39 | 40 | // improve the logging output by shortening the input path 41 | for (const obj of config) { 42 | obj.input = path.relative('.', obj.input as string); 43 | } 44 | } catch (ex) { 45 | // skip packages that throw errors (e.g., missing pkg.main) 46 | 47 | // eslint-disable-next-line no-console 48 | console.error(ex); 49 | 50 | // TODO: re-throw if this is an _unexpected_ error 51 | return null; 52 | } 53 | 54 | if (opts.watch) { 55 | // don't clear the screen during watch 56 | for (const obj of config) { 57 | // eslint-disable-next-line @typescript-eslint/ban-ts-ignore 58 | // @ts-ignore (missing rollup type, totally works mr. typescript) 59 | obj.watch = { 60 | clearScreen: false, 61 | }; 62 | } 63 | } else if (await isUpToDate(opts, config)) { 64 | // no changes, don't rebuild 65 | return null; 66 | } 67 | 68 | return config; 69 | } 70 | -------------------------------------------------------------------------------- /.eslintrc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | extends: 3 | - plugin:zillow/recommended 4 | - plugin:zillow/jest 5 | - plugin:import/typescript 6 | - plugin:node/recommended 7 | env: 8 | browser: false 9 | node: true 10 | plugins: 11 | - node 12 | rules: 13 | # prettier handles max-len fine 14 | max-len: 'off' 15 | no-restricted-syntax: 16 | - error 17 | - ForInStatement 18 | # allows ForOfStatement 19 | - LabeledStatement 20 | - WithStatement 21 | padding-line-between-statements: 22 | - error 23 | # blank line before return statements 24 | - { blankLine: 'always', prev: '*', next: 'return' } 25 | # blank line after const/let statement(s) 26 | - { blankLine: 'always', prev: ['const', 'let'], next: '*' } 27 | - { blankLine: 'any', prev: ['const', 'let'], next: ['const', 'let'] } 28 | # blank line before and after multi-line block-like statements 29 | - { blankLine: 'always', prev: '*', next: 'multiline-block-like' } 30 | - { blankLine: 'always', prev: 'multiline-block-like', next: '*' } 31 | prefer-object-spread: 'off' 32 | strict: 'off' 33 | # extend missing eslint-plugin-zillow patterns 34 | zillow/import/no-extraneous-dependencies: 35 | - error 36 | - devDependencies: ['**/test/**'] 37 | root: true 38 | settings: 39 | # effectively extending import/typescript config 40 | 'import/extensions': ['.ts', '.d.ts', '.js', '.json'] 41 | 'import/parsers': 42 | '@typescript-eslint/parser': ['.ts', '.d.ts'] 43 | 'import/resolver': 44 | node: 45 | extensions: ['.ts', '.d.ts', '.js', '.json'] 46 | 'eslint-import-resolver-typescript': true 47 | # avoid eslint-plugin-node tripping over TS 48 | node: 49 | tryExtensions: ['.ts', '.js'] 50 | overrides: 51 | - files: '**/test/**' 52 | env: 53 | jest: true 54 | - files: '*.ts' 55 | parser: '@typescript-eslint/parser' 56 | plugins: 57 | - '@typescript-eslint' 58 | extends: 59 | - 'plugin:@typescript-eslint/recommended' 60 | rules: 61 | node/no-unsupported-features/es-syntax: 62 | - error 63 | - ignores: 64 | - modules 65 | # typescript completely bamboozles these rules 66 | zillow/import/newline-after-import: 'off' 67 | zillow/import/first: 'off' 68 | -------------------------------------------------------------------------------- /packages/pectin-api/lib/is-up-to-date.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import * as util from 'util'; 4 | import globby = require('globby'); 5 | 6 | import { RollupOptions } from 'rollup'; 7 | 8 | const statAsync = util.promisify(fs.stat); 9 | 10 | export async function isUpToDate( 11 | opts: { cwd?: string }, 12 | config: RollupOptions | RollupOptions[] 13 | ): Promise { 14 | // back-compat for old signature 15 | if (Array.isArray(config)) { 16 | // eslint-disable-next-line no-param-reassign 17 | [config] = config; 18 | } 19 | 20 | // only need to test one output since all are built simultaneously 21 | /* eslint-disable @typescript-eslint/no-non-null-assertion */ 22 | const firstOutput = Array.isArray(config.output) 23 | ? config.output[0] 24 | : /* istanbul ignore next */ config.output!; 25 | const outFile = firstOutput.dir 26 | ? path.join(firstOutput.dir, firstOutput.entryFileNames!) 27 | : firstOutput.file!; 28 | /* eslint-enable */ 29 | 30 | // short-circuit if output hasn't been built yet 31 | let outputStat: fs.Stats; 32 | 33 | try { 34 | outputStat = await statAsync(outFile); 35 | } catch (ex) { 36 | // always build when output dir is missing 37 | return false; 38 | } 39 | 40 | const matchers = [ 41 | // include all .js and .jsx files 42 | '**/*.@(js|jsx|ts|tsx)', 43 | // except *.test.js and *-test.js 44 | '!**/*@(.|-)test.js', 45 | // ignoring anything under __tests__ 46 | '!**/__tests__/**', 47 | ]; 48 | 49 | // re-resolve cwd so logging-friendly relative paths don't muck things up 50 | const cwd = path.resolve(path.dirname(config.input as string)); 51 | 52 | if (cwd === opts.cwd) { 53 | // a "rooted" module needs to ignore output, test, & node_modules 54 | const outputDir = path.relative(opts.cwd, path.dirname(outFile)); 55 | 56 | matchers.push(`!${outputDir}/**`, '!node_modules/**', '!test/**'); 57 | } 58 | 59 | // gather fs.Stat objects for mtime comparison 60 | const results = await globby(matchers, { cwd, stats: true }); 61 | // eslint-disable-next-line @typescript-eslint/ban-ts-ignore 62 | // @ts-ignore (it works, trust me mr. typescript) 63 | const fileStats = results.map(obj => obj.stats); 64 | const lastBuilt = outputStat.mtime.getTime(); 65 | 66 | return fileStats.every(fileStat => fileStat.mtime.getTime() <= lastBuilt); 67 | } 68 | -------------------------------------------------------------------------------- /packages/rollup-plugin-subpath-externals/lib/rollup-plugin-subpath-externals.ts: -------------------------------------------------------------------------------- 1 | import builtins = require('builtin-modules'); 2 | import dotProp = require('dot-prop'); 3 | 4 | import { CoreProperties as PackageManifest } from '@schemastore/package'; 5 | import { Plugin, OutputOptions, IsExternal, InputOptions } from 'rollup'; 6 | 7 | // ensure subpath imports (lodash, babel-runtime) are also externalized 8 | export default function subpathExternals(pkg: PackageManifest, output?: OutputOptions): Plugin { 9 | const external: string[] = dotProp.get(pkg, 'rollup.external', []); 10 | const bundled: string[] = dotProp.get(pkg, 'rollup.bundle', []); 11 | const { format } = output || {}; 12 | const { dependencies = {}, peerDependencies = {} } = pkg; 13 | 14 | let pkgDeps: string[]; 15 | 16 | if (external.length) { 17 | pkgDeps = external; 18 | } else if (format === 'umd') { 19 | pkgDeps = Object.keys(peerDependencies); 20 | } else { 21 | pkgDeps = Object.keys(dependencies).concat(Object.keys(peerDependencies)); 22 | } 23 | 24 | if (bundled.length) { 25 | const inlined = new Set(bundled); 26 | 27 | pkgDeps = pkgDeps.filter(dep => !inlined.has(dep)); 28 | } 29 | 30 | // subpath imports always begin with module name (never on builtins) 31 | const subPathImport = new RegExp(`^(${pkgDeps.join('|')})/`); 32 | 33 | // rollup-plugin-node-resolve emits silly warnings even with preferBuiltins: true 34 | const resolvedExternals = new Set(pkgDeps.concat(builtins)); 35 | 36 | const externalPredicate: IsExternal = (id, _parentId, isResolved) => { 37 | if (isResolved === true) { 38 | // early return when the work has already been done 39 | return resolvedExternals.has(id); 40 | } 41 | 42 | if (id[0] === '.') { 43 | // always ignore relative imports 44 | return false; 45 | } 46 | 47 | if (resolvedExternals.has(id)) { 48 | // short-circuit for exact matches 49 | return true; 50 | } 51 | 52 | if (subPathImport.test(id)) { 53 | // enable subsequent short-circuit 54 | resolvedExternals.add(id); 55 | 56 | return true; 57 | } 58 | 59 | return false; 60 | }; 61 | 62 | return { 63 | name: 'subpath-externals', 64 | options: (opts): InputOptions => { 65 | // eslint-disable-next-line no-param-reassign 66 | opts.external = externalPredicate; 67 | 68 | return opts; 69 | }, 70 | }; 71 | } 72 | -------------------------------------------------------------------------------- /packages/pectin/lib/pectin-cli.ts: -------------------------------------------------------------------------------- 1 | import fs = require('fs'); 2 | import path = require('path'); 3 | import log = require('npmlog'); 4 | import yargs = require('yargs/yargs'); 5 | import { findConfigs } from '@pectin/api'; 6 | import { invokeRollup } from './invoke-rollup'; 7 | 8 | log.heading = 'pectin'; 9 | 10 | async function handler(argv: { 11 | concurrency: number; 12 | cwd: string; 13 | watch: boolean; 14 | _: string[]; 15 | }): Promise { 16 | const { version } = JSON.parse( 17 | fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf8') 18 | ); 19 | 20 | log.notice('cli', `v${version}`); 21 | 22 | if (argv.watch) { 23 | // always runs, no matter what 24 | log.info('watching', 'packages'); 25 | invokeRollup(argv); 26 | } else if ((await findConfigs(argv)).length === 0) { 27 | // nothing to do, avoid rollup error 28 | log.info('skipping', 'packages unchanged since last build'); 29 | } else { 30 | // (possibly) incremental build 31 | log.info('building', 'packages'); 32 | invokeRollup(argv); 33 | } 34 | } 35 | 36 | // eslint-disable-next-line @typescript-eslint/explicit-function-return-type 37 | export default function CLI(argv?: string[], cwd?: string) { 38 | return yargs(argv, cwd) 39 | .usage( 40 | '$0', 41 | // eslint-disable-next-line @typescript-eslint/ban-ts-ignore 42 | // @ts-ignore (typescript can't handle ...string concatenation?!?) 43 | 'Execute incremental rollup builds on all monorepo packages.\n' + 44 | 'Any additional (unknown) arguments are passed to the rollup CLI.', 45 | () => { 46 | /* no-op builder */ 47 | }, 48 | handler 49 | ) 50 | .parserConfiguration({ 51 | 'unknown-options-as-args': true, 52 | }) 53 | .options({ 54 | w: { 55 | alias: 'watch', 56 | description: 'Rebuild packages on change', 57 | type: 'boolean', 58 | }, 59 | cwd: { 60 | description: 'Current working directory', 61 | defaultDescription: 'process.cwd()', 62 | default: (): string => process.cwd(), 63 | }, 64 | concurrency: { 65 | description: 'Number of concurrent filesystem tasks', 66 | defaultDescription: '# of CPUs', 67 | type: 'number', 68 | }, 69 | }) 70 | .alias('h', 'help') 71 | .alias('v', 'version'); 72 | } 73 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pectin-monorepo", 3 | "version": "0.0.0-ignore", 4 | "description": "Pectin makes fruit-rollups stronger", 5 | "author": "Daniel Stockman ", 6 | "homepage": "https://github.com/evocateur/pectin#readme", 7 | "license": "ISC", 8 | "private": true, 9 | "engines": { 10 | "npm": ">=5.6.0", 11 | "node": ">=8.9" 12 | }, 13 | "bin": { 14 | "pectin": "packages/pectin-cli/bin/pectin" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/evocateur/pectin.git" 19 | }, 20 | "scripts": { 21 | "prepare": "npm run build", 22 | "build": "tsc --build --verbose", 23 | "lint": "eslint --cache --cache-location ./node_modules/.cache/eslint --ignore-path .gitignore --ext js,ts .", 24 | "pretest": "npm run lint", 25 | "test": "jest --coverage" 26 | }, 27 | "husky": { 28 | "hooks": { 29 | "pre-commit": "lint-staged" 30 | } 31 | }, 32 | "lint-staged": { 33 | "*.{yaml,yml}": [ 34 | "prettier --write", 35 | "git add" 36 | ], 37 | "*.[jt]s": [ 38 | "eslint --fix", 39 | "jest --find-related-tests", 40 | "git add" 41 | ] 42 | }, 43 | "dependencies": { 44 | "@pectin/api": "file:packages/pectin-api", 45 | "@pectin/babelrc": "file:packages/pectin-babelrc", 46 | "@pectin/core": "file:packages/pectin-core", 47 | "pectin": "file:packages/pectin", 48 | "rollup-config-pectin": "file:packages/rollup-config-pectin", 49 | "rollup-plugin-main-entry": "file:packages/rollup-plugin-main-entry", 50 | "rollup-plugin-subpath-externals": "file:packages/rollup-plugin-subpath-externals" 51 | }, 52 | "devDependencies": { 53 | "@babel/core": "^7.7.4", 54 | "@babel/preset-env": "^7.7.4", 55 | "@babel/preset-react": "^7.7.4", 56 | "@babel/runtime": "^7.7.4", 57 | "@babel/runtime-corejs2": "^7.7.4", 58 | "@babel/runtime-corejs3": "^7.7.4", 59 | "@lerna/project": "^3.18.0", 60 | "@lerna/run-topologically": "^3.18.5", 61 | "@schemastore/package": "^0.0.5", 62 | "@types/clone-deep": "^4.0.1", 63 | "@types/cosmiconfig": "^5.0.3", 64 | "@types/jest": "^24.0.23", 65 | "@types/npm-package-arg": "^6.1.0", 66 | "@types/npmlog": "^4.1.2", 67 | "@types/touch": "^3.1.1", 68 | "@typescript-eslint/eslint-plugin": "^2.10.0", 69 | "@typescript-eslint/parser": "^2.10.0", 70 | "babel-core": "^7.0.0-bridge.0", 71 | "builtin-modules": "^3.1.0", 72 | "camelcase": "^5.3.1", 73 | "chalk": "^2.4.2", 74 | "eslint": "^6.7.2", 75 | "eslint-import-resolver-typescript": "^2.0.0", 76 | "eslint-plugin-node": "^10.0.0", 77 | "eslint-plugin-zillow": "^3.5.1", 78 | "husky": "^3.1.0", 79 | "jest": "^24.9.0", 80 | "lerna": "^3.19.0", 81 | "lint-staged": "^9.5.0", 82 | "p-map": "^3.0.0", 83 | "prettier": "^1.19.1", 84 | "prettier-config-zillow": "^1.1.1", 85 | "resolve-from": "^5.0.0", 86 | "rollup": "^1.27.8", 87 | "tacks": "^1.3.0", 88 | "tempy": "^0.3.0", 89 | "touch": "^3.1.0", 90 | "ts-jest": "^24.2.0", 91 | "typescript": "^3.7.3", 92 | "yargs": "^14.2.2" 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /packages/pectin/test/pectin-cli.test.ts: -------------------------------------------------------------------------------- 1 | import { mocked } from 'ts-jest/utils'; 2 | import { RollupOptions } from 'rollup'; 3 | import { Arguments } from 'yargs'; 4 | import log = require('npmlog'); 5 | import * as api from '@pectin/api'; 6 | import { invokeRollup } from '../lib/invoke-rollup'; 7 | import cli from '../lib/pectin-cli'; 8 | 9 | const findConfigs = mocked(api.findConfigs); 10 | 11 | // silence logging 12 | log.level = 'silent'; 13 | 14 | // implementation tested elsewhere 15 | jest.mock('@pectin/api'); 16 | 17 | findConfigs.mockImplementation(() => 18 | Promise.resolve([ 19 | /* no changes */ 20 | ]) 21 | ); 22 | 23 | // in kat we trust 24 | jest.mock('../lib/invoke-rollup'); 25 | 26 | // helper method allows async handler to resolve 27 | const run = (...args: string[]): Promise => 28 | new Promise((resolve, reject) => 29 | cli() 30 | .exitProcess(false) 31 | .fail((msg: string, err: Error) => { 32 | setImmediate(() => reject(err)); 33 | }) 34 | .parse(args, (err: Error, argv: Arguments) => { 35 | // I have no idea why Jest + Node v8.x needs this wrapper 36 | // Without it, all the mock call assertions fail because the 37 | // call registration has not been made yet? 38 | // Basically, it's resolving "too quickly", as far as I can tell 39 | setImmediate(() => resolve(argv)); 40 | }) 41 | ); 42 | 43 | describe('pectin-cli', () => { 44 | it('calls rollup with arguments', async () => { 45 | findConfigs.mockResolvedValueOnce([{ input: 'changed' }] as RollupOptions[]); 46 | 47 | const argv = await run(); 48 | 49 | expect(invokeRollup).lastCalledWith(argv); 50 | }); 51 | 52 | it('does not call rollup when targets unchanged', async () => { 53 | await run(); 54 | 55 | expect(invokeRollup).not.toBeCalled(); 56 | }); 57 | 58 | it('passes --watch to rollup', async () => { 59 | const argv = await run('-w'); 60 | 61 | expect(argv).toHaveProperty('watch', true); 62 | expect(invokeRollup).lastCalledWith(argv); 63 | }); 64 | 65 | it('passes --cwd to pectin', async () => { 66 | findConfigs.mockResolvedValueOnce([{ input: 'changed' }] as RollupOptions[]); 67 | 68 | const argv = await run('--cwd', 'foo/bar'); 69 | 70 | expect(argv).toHaveProperty('cwd', 'foo/bar'); 71 | expect(invokeRollup).lastCalledWith(argv); 72 | }); 73 | 74 | it('passes --concurrency to pectin', async () => { 75 | findConfigs.mockResolvedValueOnce([{ input: 'changed' }] as RollupOptions[]); 76 | 77 | // process.argv members are always strings 78 | const argv = await run('--concurrency', '1'); 79 | 80 | expect(argv).toHaveProperty('concurrency', 1); 81 | expect(invokeRollup).lastCalledWith(argv); 82 | }); 83 | 84 | it('passes unknown arguments to rollup', async () => { 85 | const argv = await run('--foo', '--bar', '--watch'); 86 | 87 | expect(argv).toHaveProperty('_', ['--foo', '--bar']); 88 | expect(invokeRollup).lastCalledWith(argv); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at daniel.stockman@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | -------------------------------------------------------------------------------- /packages/pectin-core/lib/getPlugins.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/ban-ts-ignore */ 2 | import dotProp = require('dot-prop'); 3 | // @ts-ignore pending migration to typescript 4 | import babel = require('rollup-plugin-babel'); 5 | import commonjs = require('rollup-plugin-commonjs'); 6 | // @ts-ignore (TODO: migrate to @rollup/plugin-json) 7 | import json = require('rollup-plugin-json'); 8 | import nodeResolve = require('rollup-plugin-node-resolve'); 9 | import mainEntry from 'rollup-plugin-main-entry'; 10 | import replace = require('rollup-plugin-replace'); 11 | import subpathExternals from 'rollup-plugin-subpath-externals'; 12 | // @ts-ignore pending migration to typescript(?) 13 | import svg = require('rollup-plugin-svg'); 14 | import { terser } from 'rollup-plugin-terser'; 15 | import babelrc from '@pectin/babelrc'; 16 | /* eslint-enable */ 17 | 18 | import { CoreProperties as PackageManifest } from '@schemastore/package'; 19 | import { Plugin } from 'rollup'; 20 | import { RollupOutputOptions } from './getOutput'; 21 | 22 | export async function getPlugins( 23 | pkg: PackageManifest, 24 | cwd: string, 25 | output: RollupOutputOptions 26 | ): Promise { 27 | const env: string | undefined = dotProp.get(output, 'env'); 28 | const fmt: string | undefined = dotProp.get(output, 'format'); 29 | const min = fmt === 'umd' && env === 'production'; 30 | const rc = await babelrc(pkg, cwd, output); 31 | 32 | return [ 33 | mainEntry(pkg, cwd), 34 | subpathExternals(pkg, output), 35 | // https://github.com/rollup/rollup-plugin-node-resolve#usage 36 | // eslint-disable-next-line @typescript-eslint/ban-ts-ignore 37 | // @ts-ignore (it fucking works, goddammit) 38 | nodeResolve({ 39 | preferBuiltins: true, 40 | // https://github.com/rollup/rollup-plugin-node-resolve/pull/151 41 | extensions: ['.mjs', '.js', '.jsx', '.json', '.node', '.ts', '.tsx'], 42 | // https://github.com/rollup/rollup-plugin-node-resolve/pull/182 43 | mainFields: [ 44 | 'module', 45 | // just in case dependencies have missed the memo 46 | 'jsnext:main', 47 | 'main', 48 | ], 49 | }), 50 | // https://github.com/rollup/rollup-plugin-replace#usage 51 | // eslint-disable-next-line @typescript-eslint/ban-ts-ignore 52 | // @ts-ignore (it fucking works, goddammit) 53 | replace( 54 | Object.assign(env ? { 'process.env.NODE_ENV': JSON.stringify(env) } : {}, { 55 | 'process.env.BROWSER': JSON.stringify(output.browser || false), 56 | 'process.env.VERSION': JSON.stringify(pkg.version), 57 | }) 58 | ), 59 | // https://github.com/rollup/rollup-plugin-json#usage 60 | json(), 61 | // https://github.com/antony/rollup-plugin-svg 62 | dotProp.get(pkg, 'rollup.inlineSVG') && svg(), 63 | // https://github.com/rollup/rollup-plugin-babel#configuring-babel 64 | babel(rc), 65 | // https://github.com/rollup/rollup-plugin-commonjs#usage 66 | // eslint-disable-next-line @typescript-eslint/ban-ts-ignore 67 | // @ts-ignore (it fucking works, goddammit) 68 | commonjs(), 69 | min && 70 | terser({ 71 | /* eslint-disable @typescript-eslint/camelcase */ 72 | // https://github.com/terser-js/terser#minify-options 73 | compress: { 74 | pure_getters: true, 75 | unsafe: true, 76 | unsafe_comps: true, 77 | }, 78 | mangle: { 79 | keep_classnames: true, 80 | keep_fnames: true, 81 | }, 82 | /* eslint-enable @typescript-eslint/camelcase */ 83 | }), 84 | ].filter(x => Boolean(x)); 85 | } 86 | -------------------------------------------------------------------------------- /packages/pectin-core/lib/getOutput.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-non-null-assertion */ 2 | import path = require('path'); 3 | import camelCase = require('camelcase'); 4 | import npa = require('npm-package-arg'); 5 | import dotProp = require('dot-prop'); 6 | 7 | import { CoreProperties as PackageManifest } from '@schemastore/package'; 8 | import { OutputOptions } from 'rollup'; 9 | 10 | export interface RollupOutputOptions extends OutputOptions { 11 | /** An ad-hoc property to indicate this is a browser build */ 12 | browser?: boolean; 13 | 14 | /** An ad-hoc property to customize the node environment */ 15 | env?: string; 16 | } 17 | 18 | function safeName(name: string): string { 19 | const spec = npa(name); 20 | 21 | return spec.scope ? spec.name!.substr(spec.name!.indexOf('/') + 1) : spec.name!; 22 | } 23 | 24 | function nameToPascalCase(str: string): string { 25 | const name = safeName(str); 26 | 27 | return camelCase(name, { pascalCase: true }); 28 | } 29 | 30 | export function getOutput(pkg: PackageManifest, cwd: string): RollupOutputOptions[] { 31 | const output: RollupOutputOptions[] = []; 32 | 33 | // generated chunks as of rollup v0.68.0 need chunkFileNames, not entryFileNames 34 | const chunkFileNames = dotProp.get(pkg, 'rollup.chunkFileNames', '[name]-[hash].[format].js'); 35 | const entryFileNames = dotProp.get(pkg, 'rollup.entryFileNames', '[name].[format].js'); 36 | 37 | output.push({ 38 | format: 'cjs', 39 | dir: path.dirname(path.resolve(cwd, pkg.main!)), 40 | chunkFileNames, 41 | // only one entry point, thus no pattern is required 42 | entryFileNames: path.basename(pkg.main!), 43 | }); 44 | 45 | if (pkg.module) { 46 | output.push({ 47 | format: 'esm', 48 | dir: path.dirname(path.resolve(cwd, pkg.module)), 49 | chunkFileNames, 50 | entryFileNames, 51 | }); 52 | } 53 | 54 | // @see https://github.com/defunctzombie/package-browser-field-spec 55 | if (typeof pkg.browser === 'string') { 56 | // alternative main (basic) 57 | output.push({ 58 | file: path.resolve(cwd, pkg.browser), 59 | format: 'cjs', 60 | browser: true, 61 | }); 62 | } else if (pkg.browser) { 63 | // specific files (advanced) 64 | output.push( 65 | pkg.browser[pkg.main!] && { 66 | file: path.resolve(cwd, pkg.browser[pkg.main!]), 67 | format: 'cjs', 68 | browser: true, 69 | } 70 | ); 71 | output.push( 72 | pkg.browser[pkg.module!] && { 73 | file: path.resolve(cwd, pkg.browser[pkg.module!]), 74 | format: 'esm', 75 | browser: true, 76 | } 77 | ); 78 | } 79 | 80 | if (pkg.unpkg) { 81 | output.push({ 82 | file: path.resolve(cwd, pkg.unpkg.replace(/(\.min)?\.js$/, '.dev.js')), 83 | format: 'umd', 84 | env: 'development', 85 | }); 86 | output.push({ 87 | file: path.resolve(cwd, pkg.unpkg), 88 | format: 'umd', 89 | sourcemap: true, 90 | env: 'production', 91 | }); 92 | } 93 | 94 | return output 95 | .filter(x => Boolean(x)) 96 | .map(obj => { 97 | const extra: RollupOutputOptions = { 98 | exports: obj.format === 'esm' ? 'named' : 'auto', 99 | }; 100 | 101 | if (obj.format === 'umd') { 102 | extra.name = nameToPascalCase(pkg.name!); 103 | extra.globals = Object.keys(pkg.peerDependencies || {}).reduce( 104 | (acc: { [dep: string]: string }, dep: string) => { 105 | acc[dep] = nameToPascalCase(dep); 106 | 107 | return acc; 108 | }, 109 | {} 110 | ); 111 | extra.indent = false; 112 | } 113 | 114 | return Object.assign(obj, extra); 115 | }); 116 | } 117 | -------------------------------------------------------------------------------- /packages/rollup-plugin-main-entry/test/rollup-plugin-main-entry.test.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import { rollup, Plugin, RollupBuild, OutputChunk, PreRenderedChunk } from 'rollup'; 3 | import mainEntry from '../lib/rollup-plugin-main-entry'; 4 | 5 | function stubInput(relativeFilePath: string): Plugin { 6 | const fileName = path.resolve(relativeFilePath); 7 | 8 | return { 9 | name: 'stub-input', 10 | resolveId: (id): string => { 11 | if (id === fileName) { 12 | return fileName; 13 | } 14 | 15 | return null; 16 | }, 17 | load: (id): string => { 18 | if (id === fileName) { 19 | return 'export const theAnswer = 42;'; 20 | } 21 | 22 | return null; 23 | }, 24 | }; 25 | } 26 | 27 | async function getEntryChunk(bundle: RollupBuild): Promise { 28 | const { output } = await bundle.generate({ format: 'esm' }); 29 | 30 | // jesus this is convoluted. apparently interface extension only works for one hop? 31 | return (output as OutputChunk[]).find(chunk => (chunk as PreRenderedChunk).isEntry); 32 | } 33 | 34 | describe('rollup-plugin-main-entry', () => { 35 | it('throws an error when no pkg.main supplied', async () => { 36 | try { 37 | await rollup({ 38 | plugins: [mainEntry({ name: 'oops' })], 39 | }); 40 | } catch (err) { 41 | expect(err).toMatchInlineSnapshot( 42 | `[TypeError: required field 'main' missing in package.json]` 43 | ); 44 | } 45 | }); 46 | 47 | it('provides cwd-resolved opts.input from rebased pkg.main', async () => { 48 | const bundle = await rollup({ 49 | plugins: [stubInput('src/foo.js'), mainEntry({ main: 'lib/foo.js' })], 50 | }); 51 | const chunk = await getEntryChunk(bundle); 52 | 53 | expect(chunk.exports).toContain('theAnswer'); 54 | }); 55 | 56 | it('accepts custom rollup.rootDir option', async () => { 57 | const bundle = await rollup({ 58 | plugins: [ 59 | stubInput('modules/foo.js'), 60 | mainEntry({ 61 | main: 'lib/foo.js', 62 | rollup: { 63 | rootDir: 'modules', 64 | }, 65 | }), 66 | ], 67 | }); 68 | const chunk = await getEntryChunk(bundle); 69 | 70 | expect(chunk.exports).toContain('theAnswer'); 71 | }); 72 | 73 | it('preserves rootDir default in rollup package config', async () => { 74 | const bundle = await rollup({ 75 | plugins: [ 76 | stubInput('src/foo.js'), 77 | mainEntry({ 78 | main: 'lib/foo.js', 79 | rollup: { 80 | foo: true, 81 | }, 82 | }), 83 | ], 84 | }); 85 | const chunk = await getEntryChunk(bundle); 86 | 87 | expect(chunk.exports).toContain('theAnswer'); 88 | }); 89 | 90 | it('accepts custom cwd parameter', async () => { 91 | const bundle = await rollup({ 92 | plugins: [ 93 | stubInput('/bar/src/foo.js'), 94 | mainEntry( 95 | { 96 | main: 'lib/foo.js', 97 | }, 98 | '/bar' 99 | ), 100 | ], 101 | }); 102 | const chunk = await getEntryChunk(bundle); 103 | 104 | expect(chunk.exports).toContain('theAnswer'); 105 | }); 106 | 107 | it('does not overwrite existing opts.input', async () => { 108 | const bundle = await rollup({ 109 | input: path.resolve('existing.js'), 110 | plugins: [ 111 | stubInput('existing.js'), 112 | mainEntry({ 113 | main: 'lib/foo.js', 114 | }), 115 | ], 116 | }); 117 | const chunk = await getEntryChunk(bundle); 118 | 119 | expect(chunk.exports).toContain('theAnswer'); 120 | }); 121 | }); 122 | -------------------------------------------------------------------------------- /packages/pectin-babelrc/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [3.3.3](https://github.com/evocateur/pectin/compare/@pectin/babelrc@3.3.2...@pectin/babelrc@3.3.3) (2019-08-21) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **babelrc:** Provide version to runtime transform config ([16affba](https://github.com/evocateur/pectin/commit/16affba)) 12 | 13 | 14 | 15 | 16 | 17 | ## [3.3.2](https://github.com/evocateur/pectin/compare/@pectin/babelrc@3.3.1...@pectin/babelrc@3.3.2) (2019-08-06) 18 | 19 | 20 | ### Bug Fixes 21 | 22 | * **babelrc:** Assign explicit cwd to rollup-specific config ([e8fb759](https://github.com/evocateur/pectin/commit/e8fb759)) 23 | * **babelrc:** Never mutate require-cached config ([88da621](https://github.com/evocateur/pectin/commit/88da621)) 24 | 25 | 26 | 27 | 28 | 29 | ## [3.3.1](https://github.com/evocateur/pectin/compare/@pectin/babelrc@3.3.0...@pectin/babelrc@3.3.1) (2019-08-02) 30 | 31 | 32 | ### Bug Fixes 33 | 34 | * **config:** Throw an error if a Babel config cannot be located ([99c3f43](https://github.com/evocateur/pectin/commit/99c3f43)) 35 | 36 | 37 | 38 | 39 | 40 | # [3.3.0](https://github.com/evocateur/pectin/compare/v3.2.0...v3.3.0) (2019-06-17) 41 | 42 | 43 | ### Features 44 | 45 | * Ssupport typescript extensions ([#8](https://github.com/evocateur/pectin/issues/8)) ([b0ee9f9](https://github.com/evocateur/pectin/commit/b0ee9f9)) 46 | 47 | 48 | 49 | 50 | 51 | # [3.2.0](https://github.com/evocateur/pectin/compare/v3.1.1...v3.2.0) (2019-05-03) 52 | 53 | 54 | ### Features 55 | 56 | * **babelrc:** Recognize `@babel/runtime-corejs3` when injecting runtime transform ([43c749f](https://github.com/evocateur/pectin/commit/43c749f)) 57 | 58 | 59 | 60 | 61 | 62 | # [3.1.0](https://github.com/evocateur/pectin/compare/v3.0.1...v3.1.0) (2019-04-09) 63 | 64 | 65 | ### Bug Fixes 66 | 67 | * **babelrc:** Update `cosmiconfig` dependency range to avoid audit warning ([ccfbd5d](https://github.com/evocateur/pectin/commit/ccfbd5d)) 68 | 69 | 70 | 71 | 72 | 73 | # [3.0.0](https://github.com/evocateur/pectin/compare/v2.6.0...v3.0.0) (2019-01-01) 74 | 75 | 76 | ### Features 77 | 78 | * Rollup 1.0 🎉 ([938db4e](https://github.com/evocateur/pectin/commit/938db4e)) 79 | 80 | 81 | ### BREAKING CHANGES 82 | 83 | * Rollup 0.x is no longer supported. 84 | 85 | 86 | 87 | 88 | 89 | # [2.2.0](https://github.com/evocateur/pectin/compare/v2.1.2...v2.2.0) (2018-11-07) 90 | 91 | 92 | ### Features 93 | 94 | * **babelrc:** Ensure dynamic `import()` syntax is enabled for ESM format ([44626ad](https://github.com/evocateur/pectin/commit/44626ad)) 95 | 96 | 97 | 98 | 99 | 100 | ## [2.1.2](https://github.com/evocateur/pectin/compare/v2.1.1...v2.1.2) (2018-10-19) 101 | 102 | 103 | ### Bug Fixes 104 | 105 | * **babelrc:** Handle [@babel](https://github.com/babel)/runtime-corejs2 dependency correctly ([ffc9f19](https://github.com/evocateur/pectin/commit/ffc9f19)) 106 | 107 | 108 | 109 | 110 | 111 | ## [2.1.1](https://github.com/evocateur/pectin/compare/v2.1.0...v2.1.1) (2018-10-16) 112 | 113 | 114 | ### Bug Fixes 115 | 116 | * **babelrc:** Do not duplicate existing runtime transform ([aab8e4e](https://github.com/evocateur/pectin/commit/aab8e4e)) 117 | 118 | 119 | 120 | 121 | 122 | # [2.1.0](https://github.com/evocateur/pectin/compare/v2.0.0...v2.1.0) (2018-10-16) 123 | 124 | 125 | ### Features 126 | 127 | * **babelrc:** Accept optional format config that controls value of useESModules option passed to runtime transform ([8e7622d](https://github.com/evocateur/pectin/commit/8e7622d)) 128 | 129 | 130 | 131 | 132 | 133 | # [2.0.0](https://github.com/evocateur/pectin/compare/v1.3.0...v2.0.0) (2018-10-10) 134 | 135 | 136 | ### Features 137 | 138 | * Upgrade to Babel 7 ([#2](https://github.com/evocateur/pectin/issues/2)) ([3b460ba](https://github.com/evocateur/pectin/commit/3b460ba)), closes [#1](https://github.com/evocateur/pectin/issues/1) 139 | 140 | 141 | ### BREAKING CHANGES 142 | 143 | * Babel 6 is no longer supported. Consult https://babeljs.io/docs/en/v7-migration for upgrade steps. 144 | 145 | 146 | 147 | 148 | 149 | 150 | # 1.0.0 (2018-08-31) 151 | 152 | 153 | ### Features 154 | 155 | * open source ([ce2d5cb](https://github.com/evocateur/pectin/commit/ce2d5cb)) 156 | -------------------------------------------------------------------------------- /packages/pectin-babelrc/lib/pectin-babelrc.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import cloneDeep = require('clone-deep'); 3 | import cosmiconfig = require('cosmiconfig'); 4 | import resolveFrom = require('resolve-from'); 5 | 6 | import { CoreProperties as PackageManifest } from '@schemastore/package'; 7 | import { OutputOptions } from 'rollup'; 8 | 9 | const explorer = cosmiconfig('babel', { 10 | // we cannot cache transform because per-package dependencies affect result 11 | searchPlaces: [ 12 | // babel 7 13 | 'babel.config.js', 14 | '.babelrc.js', 15 | // babel 6+ 16 | '.babelrc', 17 | 'package.json', 18 | ], 19 | }); 20 | 21 | function isRuntimeTransform(plugin: string): boolean { 22 | return /@babel\/(plugin-)?transform-runtime/.test(plugin); 23 | } 24 | 25 | function hasSimpleTransform(plugin: string | unknown): boolean { 26 | return typeof plugin === 'string' && isRuntimeTransform(plugin); 27 | } 28 | 29 | function hasAdvancedTransform(plugin: string[] | unknown): boolean { 30 | return Array.isArray(plugin) && isRuntimeTransform(plugin[0]); 31 | } 32 | 33 | // @see https://github.com/babel/babel/issues/10261 34 | // @see https://github.com/babel/babel/pull/10325 35 | function resolveDependencyVersion(cwd: string, depName: string): string | undefined { 36 | // we can't do a straight-up `require('@babel/runtime/package.json')` 37 | // because that doesn't respect the target package's cwd 38 | const pkgPath = resolveFrom(cwd, `${depName}/package.json`); 39 | 40 | // istanbul ignore next: undefined doesn't matter, we tried our best 41 | // eslint-disable-next-line global-require, zillow/import/no-dynamic-require 42 | return pkgPath ? require(pkgPath).version : undefined; 43 | } 44 | 45 | function ensureRuntimeHelpers( 46 | rc: cosmiconfig.Config, 47 | entryOptions: { useESModules: boolean; version: string | undefined; corejs?: number } 48 | ): void { 49 | if (rc.plugins.some(hasSimpleTransform)) { 50 | const idx = rc.plugins.findIndex(hasSimpleTransform); 51 | const name = rc.plugins[idx]; 52 | 53 | rc.plugins.splice(idx, 1, [name, entryOptions]); 54 | } else if (rc.plugins.some(hasAdvancedTransform)) { 55 | const idx = rc.plugins.findIndex(hasAdvancedTransform); 56 | const [name, config = {}] = rc.plugins[idx]; 57 | 58 | rc.plugins.splice(idx, 1, [name, { ...config, ...entryOptions }]); 59 | } else { 60 | rc.plugins.push(['@babel/plugin-transform-runtime', entryOptions]); 61 | } 62 | 63 | // eslint-disable-next-line no-param-reassign 64 | rc.runtimeHelpers = true; 65 | } 66 | 67 | function hasDynamicImportSyntax(plugin: string): boolean { 68 | return typeof plugin === 'string' && /@babel\/(plugin-)?syntax-dynamic-import/.test(plugin); 69 | } 70 | 71 | export default async function babelrc( 72 | pkg: PackageManifest, 73 | cwd: string = process.cwd(), 74 | output?: OutputOptions 75 | ): Promise { 76 | const { format = 'cjs' } = output || {}; 77 | const searchResult = await explorer.search(cwd); 78 | 79 | if (searchResult === null) { 80 | throw new Error( 81 | `Babel configuration is required for ${pkg.name}, but no config file was found.` 82 | ); 83 | } 84 | 85 | const { config, filepath } = searchResult; 86 | const deps = new Set(Object.keys(pkg.dependencies || {})); 87 | 88 | // don't mutate (potentially) cached config 89 | const rc = cloneDeep(config); 90 | 91 | // always ensure plugins array exists 92 | if (!rc.plugins) { 93 | rc.plugins = []; 94 | } 95 | 96 | // enable runtime transform when @babel/runtime found in dependencies 97 | if (deps.has('@babel/runtime')) { 98 | ensureRuntimeHelpers(rc, { 99 | useESModules: format === 'esm', 100 | version: resolveDependencyVersion(cwd, '@babel/runtime'), 101 | }); 102 | } else if (deps.has('@babel/runtime-corejs2')) { 103 | ensureRuntimeHelpers(rc, { 104 | useESModules: format === 'esm', 105 | version: resolveDependencyVersion(cwd, '@babel/runtime-corejs2'), 106 | corejs: 2, 107 | }); 108 | } else if (deps.has('@babel/runtime-corejs3')) { 109 | ensureRuntimeHelpers(rc, { 110 | useESModules: format === 'esm', 111 | version: resolveDependencyVersion(cwd, '@babel/runtime-corejs3'), 112 | corejs: 3, 113 | }); 114 | } 115 | 116 | // ensure dynamic import syntax is available 117 | if (format === 'esm' && !rc.plugins.some(hasDynamicImportSyntax)) { 118 | rc.plugins.unshift('@babel/plugin-syntax-dynamic-import'); 119 | } 120 | 121 | // babel 7 doesn't need `{ modules: false }`, just verify a preset exists 122 | if (!rc.presets) { 123 | const fileLoc = path.relative(cwd, filepath); 124 | const badConfig = 125 | path.basename(filepath) === 'package.json' 126 | ? `"babel" config block of ${fileLoc}` 127 | : fileLoc; 128 | 129 | throw new Error(`At least one preset (like @babel/preset-env) is required in ${badConfig}`); 130 | } 131 | 132 | // rollup-specific babel config 133 | rc.babelrc = false; 134 | rc.exclude = 'node_modules/**'; 135 | rc.extensions = ['.js', '.jsx', '.es6', '.es', '.mjs', '.ts', '.tsx']; 136 | rc.cwd = cwd; 137 | 138 | return rc; 139 | } 140 | -------------------------------------------------------------------------------- /packages/rollup-config-pectin/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [4.0.3](https://github.com/evocateur/pectin/compare/rollup-config-pectin@4.0.2...rollup-config-pectin@4.0.3) (2019-08-21) 7 | 8 | **Note:** Version bump only for package rollup-config-pectin 9 | 10 | 11 | 12 | 13 | 14 | ## [4.0.2](https://github.com/evocateur/pectin/compare/rollup-config-pectin@4.0.1...rollup-config-pectin@4.0.2) (2019-08-06) 15 | 16 | **Note:** Version bump only for package rollup-config-pectin 17 | 18 | 19 | 20 | 21 | 22 | ## [4.0.1](https://github.com/evocateur/pectin/compare/rollup-config-pectin@4.0.0...rollup-config-pectin@4.0.1) (2019-08-02) 23 | 24 | **Note:** Version bump only for package rollup-config-pectin 25 | 26 | 27 | 28 | 29 | 30 | # 4.0.0 (2019-08-01) 31 | 32 | 33 | ### Features 34 | 35 | * Upgrade `rollup-plugin-commonjs` & `rollup-plugin-node-resolve` ([979d7ec](https://github.com/evocateur/pectin/commit/979d7ec)) 36 | 37 | 38 | ### BREAKING CHANGES 39 | 40 | * The minimum version of the `rollup` peer dependency is now `^1.12.0`. 41 | 42 | The latest versions of `rollup-plugin-commonjs` ([changelog](https://github.com/rollup/rollup-plugin-commonjs/blob/master/CHANGELOG.md#1000)) and `rollup-plugin-node-resolve` ([changelog](https://github.com/rollup/rollup-plugin-node-resolve/blob/master/CHANGELOG.md#500-2019-05-15)) require core Rollup methods only available after [`rollup@v1.12.0`](https://github.com/rollup/rollup/blob/master/CHANGELOG.md#1120). 43 | 44 | 45 | 46 | 47 | # [3.4.0](https://github.com/evocateur/pectin/compare/v3.3.0...v3.4.0) (2019-08-01) 48 | 49 | **Note:** Version bump only for package rollup-config-pectin 50 | 51 | 52 | 53 | 54 | 55 | # [3.3.0](https://github.com/evocateur/pectin/compare/v3.2.0...v3.3.0) (2019-06-17) 56 | 57 | **Note:** Version bump only for package rollup-config-pectin 58 | 59 | 60 | 61 | 62 | 63 | # [3.2.0](https://github.com/evocateur/pectin/compare/v3.1.1...v3.2.0) (2019-05-03) 64 | 65 | **Note:** Version bump only for package rollup-config-pectin 66 | 67 | 68 | 69 | 70 | 71 | # [3.1.0](https://github.com/evocateur/pectin/compare/v3.0.1...v3.1.0) (2019-04-09) 72 | 73 | **Note:** Version bump only for package rollup-config-pectin 74 | 75 | 76 | 77 | 78 | 79 | ## [3.0.1](https://github.com/evocateur/pectin/compare/v3.0.0...v3.0.1) (2019-01-14) 80 | 81 | **Note:** Version bump only for package rollup-config-pectin 82 | 83 | 84 | 85 | 86 | 87 | # [3.0.0](https://github.com/evocateur/pectin/compare/v2.6.0...v3.0.0) (2019-01-01) 88 | 89 | 90 | ### Features 91 | 92 | * Rollup 1.0 🎉 ([938db4e](https://github.com/evocateur/pectin/commit/938db4e)) 93 | * **core:** Simplify API ([0c74c73](https://github.com/evocateur/pectin/commit/0c74c73)) 94 | 95 | 96 | ### BREAKING CHANGES 97 | 98 | * **core:** There is only a default export on pectin-core now, load your own package.json. 99 | * Rollup 0.x is no longer supported. 100 | 101 | 102 | 103 | 104 | 105 | # [2.6.0](https://github.com/evocateur/pectin/compare/v2.5.2...v2.6.0) (2018-12-27) 106 | 107 | 108 | ### Features 109 | 110 | * **core:** Enable chunking for CommonJS ([480f20d](https://github.com/evocateur/pectin/commit/480f20d)) 111 | 112 | 113 | 114 | 115 | 116 | ## [2.5.1](https://github.com/evocateur/pectin/compare/v2.5.0...v2.5.1) (2018-11-16) 117 | 118 | **Note:** Version bump only for package rollup-config-pectin 119 | 120 | 121 | 122 | 123 | 124 | # [2.5.0](https://github.com/evocateur/pectin/compare/v2.4.1...v2.5.0) (2018-11-15) 125 | 126 | **Note:** Version bump only for package rollup-config-pectin 127 | 128 | 129 | 130 | 131 | 132 | ## [2.4.1](https://github.com/evocateur/pectin/compare/v2.4.0...v2.4.1) (2018-11-15) 133 | 134 | **Note:** Version bump only for package rollup-config-pectin 135 | 136 | 137 | 138 | 139 | 140 | # [2.4.0](https://github.com/evocateur/pectin/compare/v2.3.0...v2.4.0) (2018-11-14) 141 | 142 | **Note:** Version bump only for package rollup-config-pectin 143 | 144 | 145 | 146 | 147 | 148 | # [2.3.0](https://github.com/evocateur/pectin/compare/v2.2.0...v2.3.0) (2018-11-13) 149 | 150 | 151 | ### Features 152 | 153 | * **rollup-config-pectin:** Use advanced multi-config for better ESM output ([92be659](https://github.com/evocateur/pectin/commit/92be659)) 154 | 155 | 156 | 157 | 158 | 159 | # [2.2.0](https://github.com/evocateur/pectin/compare/v2.1.2...v2.2.0) (2018-11-07) 160 | 161 | **Note:** Version bump only for package rollup-config-pectin 162 | 163 | 164 | 165 | 166 | 167 | ## [2.1.2](https://github.com/evocateur/pectin/compare/v2.1.1...v2.1.2) (2018-10-19) 168 | 169 | **Note:** Version bump only for package rollup-config-pectin 170 | 171 | 172 | 173 | 174 | 175 | ## [2.1.1](https://github.com/evocateur/pectin/compare/v2.1.0...v2.1.1) (2018-10-16) 176 | 177 | **Note:** Version bump only for package rollup-config-pectin 178 | 179 | 180 | 181 | 182 | 183 | # [2.1.0](https://github.com/evocateur/pectin/compare/v2.0.0...v2.1.0) (2018-10-16) 184 | 185 | **Note:** Version bump only for package rollup-config-pectin 186 | 187 | 188 | 189 | 190 | 191 | # [2.0.0](https://github.com/evocateur/pectin/compare/v1.3.0...v2.0.0) (2018-10-10) 192 | 193 | **Note:** Version bump only for package rollup-config-pectin 194 | 195 | 196 | 197 | 198 | 199 | # [1.3.0](https://github.com/evocateur/pectin/compare/v1.2.0...v1.3.0) (2018-10-10) 200 | 201 | **Note:** Version bump only for package rollup-config-pectin 202 | 203 | 204 | 205 | 206 | 207 | 208 | # 1.0.0 (2018-08-31) 209 | 210 | 211 | ### Features 212 | 213 | * open source ([ce2d5cb](https://github.com/evocateur/pectin/commit/ce2d5cb)) 214 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pectin 2 | 3 | > [Rollup][]-related tools for incremental transpilation of packages in [Lerna][]-based monorepos 4 | 5 | [![npm version](https://img.shields.io/npm/v/pectin.svg)](https://www.npmjs.com/package/pectin) 6 | [![Build Status](https://travis-ci.org/evocateur/pectin.svg?branch=latest)](https://travis-ci.org/evocateur/pectin) 7 | 8 | ## Getting Started 9 | 10 | The easiest way to start using Pectin is to install the CLI and run it during an npm lifecycle, such as `"prerelease"`: 11 | 12 | ```sh 13 | npm i -D pectin 14 | ``` 15 | 16 | In your monorepo's root `package.json` (aka "manifest"): 17 | 18 | ```json 19 | { 20 | "scripts": { 21 | "clean": "git clean -fdx packages", 22 | "prerelease": "npm run clean && pectin", 23 | "release": "lerna publish", 24 | "lint": "eslint .", 25 | "pretest": "pectin && npm run lint", 26 | "test": "jest" 27 | } 28 | } 29 | ``` 30 | 31 | Configured this way, you can always ensure your packages have the latest build output whenever anyone executes `npm run release` _or_ incrementally build recent changes before `npm test`. 32 | 33 | Once installed locally, you can experiment with the CLI via `npx`: 34 | 35 | ```sh 36 | npx pectin -h 37 | ``` 38 | 39 | To watch packages and rebuild on source change, pass `-w`, just like Rollup's CLI: 40 | 41 | ```sh 42 | npx pectin -w 43 | ``` 44 | 45 | ## Motivation 46 | 47 | One advantage of a [Lerna][] monorepo is that you can reduce the amount of repetition between modules by running all development-related tasks (build, lint, test, and so on) from the root of the repository instead of each package one-by-one. This works fine for tools that are capable of running over many packages simultaneously without breaking a sweat, like `jest` and `eslint`. 48 | 49 | Running Rollup builds over many different package roots, however, is a much trickier business. Pectin was built to facilitate running Rollup builds for all packages in a monorepo, with special consideration for unique monorepo circumstances such as incremental builds, npm lifecycle behavior, and per-package options. 50 | 51 | For example, it isn't always the case that _every_ package in a monorepo actually needs to be rebuilt every time the build is run. Consider running `jest --watch` in a monorepo with 15 packages, but you're only working on one. The naïve approach finds all the packages and passes all of them to Rollup, which means Rollup builds for every package. Pectin optimizes this by testing the "freshness" of the built output against the source tree and only building when a file in the source tree has a more recent change (a higher `mtime`, for filesystem wizards). 52 | 53 | Pectin's CLI was written to seamlessly wrap `rollup`. It helps avoid, among other things, Rollup's CLI emitting a warning and exiting non-zero when you pass an empty array (that is, no changes since the last build) to Rollup via the default export of `rollup.config.js`. Pectin's CLI supports all options supported by Rollup's CLI. 54 | 55 | ## Contributing 56 | 57 | Please note that this project is released with a [Contributor Code of Conduct](./CODE_OF_CONDUCT.md). 58 | By participating in this project you agree to abide by its terms. 59 | 60 | ## Packages 61 | 62 | - [`@pectin/api`](./packages/pectin-api#readme) 63 | - [`@pectin/babelrc`](./packages/pectin-babelrc#readme) 64 | - [`@pectin/core`](./packages/pectin-core#readme) 65 | - [`pectin`](./packages/pectin#readme) 66 | - [`rollup-config-pectin`](./packages/rollup-config-pectin#readme) 67 | - [`rollup-plugin-main-entry`](./packages/rollup-plugin-main-entry#readme) 68 | - [`rollup-plugin-subpath-externals`](./packages/rollup-plugin-subpath-externals#readme) 69 | 70 | ## Customizing Plugins 71 | 72 | When calling the `pectin` CLI, there is no support for adding plugins beyond those already included. 73 | However, as `pectin` is _mostly_ just a fancy wrapper around the `rollup` CLI, it is possible to generate Rollup config programmatically and simulate the "lazy build" behavior of `pectin`. 74 | 75 | First, create a `rollup.config.js` in the root of your monorepo: 76 | 77 | ```js 78 | import * as path from 'path'; 79 | import { findConfigs } from '@pectin/api'; 80 | import visualizer from 'rollup-plugin-visualizer'; 81 | 82 | export default findConfigs().then(configs => 83 | configs.map(cfg => { 84 | const { 85 | // format can be 'cjs', 'esm', or 'umd' 86 | format, 87 | // absolute directory from pkg.main, 88 | // e.g. '/packages//dist' 89 | dir: outputDir, 90 | } = cfg.output[0]; 91 | 92 | // plugins are assigned per-format, as certain 93 | // formats require different plugin configuration 94 | if (format === 'esm') { 95 | cfg.plugins.push( 96 | visualizer({ 97 | filename: path.join(outputDir, 'stats.html'), 98 | }) 99 | ); 100 | } 101 | 102 | return cfg; 103 | }) 104 | ); 105 | ``` 106 | 107 | Then change any references to `pectin` in your npm scripts to `rollup -c`: 108 | 109 | ```json 110 | { 111 | "scripts": { 112 | "build": "rollup -c || echo 'no changed packages to build, probably?'", 113 | "watch": "rollup -c -w" 114 | } 115 | } 116 | ``` 117 | 118 | The caveat highlighted by the `||` alternation above is that `rollup` will complain if the array generated by `findConfigs()` is empty, and exits non-zero. Unless caught by the `||`, `npm run build` would exit with an error. 119 | 120 | ## Ignoring Packages 121 | 122 | If you have a package that you do not want Pectin to build, you can add the following to its `package.json`: 123 | 124 | ```json 125 | "rollup": { 126 | "skip": true 127 | } 128 | ``` 129 | 130 | ## Related 131 | 132 | - [Lerna][] 133 | - [Rollup][] 134 | 135 | [lerna]: https://github.com/lerna/lerna#readme 136 | [rollup]: https://github.com/rollup/rollup#readme 137 | -------------------------------------------------------------------------------- /packages/pectin/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [3.6.1](https://github.com/evocateur/pectin/compare/pectin@3.6.0...pectin@3.6.1) (2019-10-17) 7 | 8 | **Note:** Version bump only for package pectin 9 | 10 | 11 | 12 | 13 | 14 | # [3.6.0](https://github.com/evocateur/pectin/compare/pectin@3.5.4...pectin@3.6.0) (2019-10-15) 15 | 16 | 17 | ### Features 18 | 19 | * **cli:** Bump rollup dependency to ^1.23.1 ([8072762](https://github.com/evocateur/pectin/commit/807276246814fe39377d61f2ea93be28e14301a7)) 20 | * **cli:** Upgrade yargs to v14 ([f424830](https://github.com/evocateur/pectin/commit/f42483070a5a08f1893d0852d64e8d949c8c70f3)) 21 | 22 | 23 | 24 | 25 | 26 | ## [3.5.4](https://github.com/evocateur/pectin/compare/pectin@3.5.3...pectin@3.5.4) (2019-08-21) 27 | 28 | **Note:** Version bump only for package pectin 29 | 30 | 31 | 32 | 33 | 34 | ## [3.5.3](https://github.com/evocateur/pectin/compare/pectin@3.5.2...pectin@3.5.3) (2019-08-09) 35 | 36 | 37 | ### Bug Fixes 38 | 39 | * **cli:** Resolve rollup bin from package metadata instead of hard-coded subpath ([dca84fc](https://github.com/evocateur/pectin/commit/dca84fc)) 40 | 41 | 42 | 43 | 44 | 45 | ## [3.5.2](https://github.com/evocateur/pectin/compare/pectin@3.5.1...pectin@3.5.2) (2019-08-06) 46 | 47 | **Note:** Version bump only for package pectin 48 | 49 | 50 | 51 | 52 | 53 | ## [3.5.1](https://github.com/evocateur/pectin/compare/pectin@3.5.0...pectin@3.5.1) (2019-08-02) 54 | 55 | **Note:** Version bump only for package pectin 56 | 57 | 58 | 59 | 60 | 61 | # 3.5.0 (2019-08-01) 62 | 63 | 64 | ### Features 65 | 66 | * Raise floor of rollup dependency range to ^1.12.0 ([4dbbe72](https://github.com/evocateur/pectin/commit/4dbbe72)) 67 | 68 | 69 | 70 | # [3.4.0](https://github.com/evocateur/pectin/compare/v3.3.0...v3.4.0) (2019-08-01) 71 | 72 | 73 | ### Features 74 | 75 | * **deps:** Upgrade dependencies ([923f92f](https://github.com/evocateur/pectin/commit/923f92f)) 76 | 77 | 78 | 79 | 80 | 81 | # [3.3.0](https://github.com/evocateur/pectin/compare/v3.2.0...v3.3.0) (2019-06-17) 82 | 83 | **Note:** Version bump only for package pectin 84 | 85 | 86 | 87 | 88 | 89 | # [3.2.0](https://github.com/evocateur/pectin/compare/v3.1.1...v3.2.0) (2019-05-03) 90 | 91 | **Note:** Version bump only for package pectin 92 | 93 | 94 | 95 | 96 | 97 | ## [3.1.1](https://github.com/evocateur/pectin/compare/v3.1.0...v3.1.1) (2019-04-09) 98 | 99 | 100 | ### Bug Fixes 101 | 102 | * **cli:** Rename homepage, too ([d89fc16](https://github.com/evocateur/pectin/commit/d89fc16)) 103 | 104 | 105 | 106 | 107 | 108 | # [3.1.0](https://github.com/evocateur/pectin/compare/v3.0.1...v3.1.0) (2019-04-09) 109 | 110 | 111 | ### Features 112 | 113 | * **pectin:** Rename CLI `@pectin/cli` -> `pectin` ([0a81cfe](https://github.com/evocateur/pectin/commit/0a81cfe)) 114 | 115 | 116 | 117 | 118 | 119 | ## [3.0.1](https://github.com/evocateur/pectin/compare/v3.0.0...v3.0.1) (2019-01-14) 120 | 121 | **Note:** Version bump only for package @pectin/cli 122 | 123 | 124 | 125 | 126 | 127 | # [3.0.0](https://github.com/evocateur/pectin/compare/v2.6.0...v3.0.0) (2019-01-01) 128 | 129 | 130 | ### Features 131 | 132 | * Rollup 1.0 🎉 ([938db4e](https://github.com/evocateur/pectin/commit/938db4e)) 133 | 134 | 135 | ### BREAKING CHANGES 136 | 137 | * Rollup 0.x is no longer supported. 138 | 139 | 140 | 141 | 142 | 143 | # [2.6.0](https://github.com/evocateur/pectin/compare/v2.5.2...v2.6.0) (2018-12-27) 144 | 145 | 146 | ### Features 147 | 148 | * **cli:** Upgrade rollup to ^0.68.2 ([22a0411](https://github.com/evocateur/pectin/commit/22a0411)) 149 | 150 | 151 | 152 | 153 | 154 | ## [2.5.2](https://github.com/evocateur/pectin/compare/v2.5.1...v2.5.2) (2018-12-13) 155 | 156 | **Note:** Version bump only for package @pectin/cli 157 | 158 | 159 | 160 | 161 | 162 | ## [2.5.1](https://github.com/evocateur/pectin/compare/v2.5.0...v2.5.1) (2018-11-16) 163 | 164 | **Note:** Version bump only for package @pectin/cli 165 | 166 | 167 | 168 | 169 | 170 | # [2.5.0](https://github.com/evocateur/pectin/compare/v2.4.1...v2.5.0) (2018-11-15) 171 | 172 | **Note:** Version bump only for package @pectin/cli 173 | 174 | 175 | 176 | 177 | 178 | ## [2.4.1](https://github.com/evocateur/pectin/compare/v2.4.0...v2.4.1) (2018-11-15) 179 | 180 | **Note:** Version bump only for package @pectin/cli 181 | 182 | 183 | 184 | 185 | 186 | # [2.4.0](https://github.com/evocateur/pectin/compare/v2.3.0...v2.4.0) (2018-11-14) 187 | 188 | **Note:** Version bump only for package @pectin/cli 189 | 190 | 191 | 192 | 193 | 194 | # [2.2.0](https://github.com/evocateur/pectin/compare/v2.1.2...v2.2.0) (2018-11-07) 195 | 196 | 197 | ### Features 198 | 199 | * **cli:** Upgrade rollup to ^0.67.0 ([11f3dba](https://github.com/evocateur/pectin/commit/11f3dba)) 200 | 201 | 202 | 203 | 204 | 205 | ## [2.1.2](https://github.com/evocateur/pectin/compare/v2.1.1...v2.1.2) (2018-10-19) 206 | 207 | **Note:** Version bump only for package @pectin/cli 208 | 209 | 210 | 211 | 212 | 213 | ## [2.1.1](https://github.com/evocateur/pectin/compare/v2.1.0...v2.1.1) (2018-10-16) 214 | 215 | **Note:** Version bump only for package @pectin/cli 216 | 217 | 218 | 219 | 220 | 221 | # [2.1.0](https://github.com/evocateur/pectin/compare/v2.0.0...v2.1.0) (2018-10-16) 222 | 223 | **Note:** Version bump only for package @pectin/cli 224 | 225 | 226 | 227 | 228 | 229 | # [2.0.0](https://github.com/evocateur/pectin/compare/v1.3.0...v2.0.0) (2018-10-10) 230 | 231 | **Note:** Version bump only for package @pectin/cli 232 | 233 | 234 | 235 | 236 | 237 | # [1.3.0](https://github.com/evocateur/pectin/compare/v1.2.0...v1.3.0) (2018-10-10) 238 | 239 | **Note:** Version bump only for package @pectin/cli 240 | 241 | 242 | 243 | 244 | 245 | 246 | # [1.2.0](https://github.com/evocateur/pectin/compare/v1.1.0...v1.2.0) (2018-10-03) 247 | 248 | 249 | ### Features 250 | 251 | * **cli:** Upgrade rollup to ^0.66.3 ([653ed9b](https://github.com/evocateur/pectin/commit/653ed9b)) 252 | 253 | 254 | 255 | 256 | 257 | 258 | # [1.1.0](https://github.com/evocateur/pectin/compare/v1.0.0...v1.1.0) (2018-10-03) 259 | 260 | **Note:** Version bump only for package @pectin/cli 261 | 262 | 263 | 264 | 265 | 266 | 267 | # 1.0.0 (2018-08-31) 268 | 269 | 270 | ### Features 271 | 272 | * open source ([ce2d5cb](https://github.com/evocateur/pectin/commit/ce2d5cb)) 273 | -------------------------------------------------------------------------------- /packages/pectin-api/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [4.0.5](https://github.com/evocateur/pectin/compare/@pectin/api@4.0.4...@pectin/api@4.0.5) (2019-10-17) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **api:** Generate configs in _proper_ topological order ([9ea4aa3](https://github.com/evocateur/pectin/commit/9ea4aa322884d1c1d4960154b8245307866cef77)) 12 | 13 | 14 | 15 | 16 | 17 | ## [4.0.4](https://github.com/evocateur/pectin/compare/@pectin/api@4.0.3...@pectin/api@4.0.4) (2019-10-15) 18 | 19 | 20 | ### Bug Fixes 21 | 22 | * **isUpToDate:** add ts and tsx to isUpToDate matchers ([#12](https://github.com/evocateur/pectin/issues/12)) ([6accaf8](https://github.com/evocateur/pectin/commit/6accaf82555b25047d234c2854b67536efa1d99a)) 23 | 24 | 25 | 26 | 27 | 28 | ## [4.0.3](https://github.com/evocateur/pectin/compare/@pectin/api@4.0.2...@pectin/api@4.0.3) (2019-08-21) 29 | 30 | **Note:** Version bump only for package @pectin/api 31 | 32 | 33 | 34 | 35 | 36 | ## [4.0.2](https://github.com/evocateur/pectin/compare/@pectin/api@4.0.1...@pectin/api@4.0.2) (2019-08-06) 37 | 38 | **Note:** Version bump only for package @pectin/api 39 | 40 | 41 | 42 | 43 | 44 | ## [4.0.1](https://github.com/evocateur/pectin/compare/@pectin/api@4.0.0...@pectin/api@4.0.1) (2019-08-02) 45 | 46 | 47 | ### Bug Fixes 48 | 49 | * **api:** Log errors caught during config generation instead of completely swallowing them ([26ba75b](https://github.com/evocateur/pectin/commit/26ba75b)) 50 | 51 | 52 | 53 | 54 | 55 | # 4.0.0 (2019-08-01) 56 | 57 | 58 | ### Features 59 | 60 | * Upgrade `globby` to `^10.0.1` ([c9a4475](https://github.com/evocateur/pectin/commit/c9a4475)) 61 | * Upgrade `rollup-plugin-commonjs` & `rollup-plugin-node-resolve` ([979d7ec](https://github.com/evocateur/pectin/commit/979d7ec)) 62 | 63 | 64 | ### BREAKING CHANGES 65 | 66 | * The minimum version of the `rollup` peer dependency is now `^1.12.0`. 67 | 68 | The latest versions of `rollup-plugin-commonjs` ([changelog](https://github.com/rollup/rollup-plugin-commonjs/blob/master/CHANGELOG.md#1000)) and `rollup-plugin-node-resolve` ([changelog](https://github.com/rollup/rollup-plugin-node-resolve/blob/master/CHANGELOG.md#500-2019-05-15)) require core Rollup methods only available after [`rollup@v1.12.0`](https://github.com/rollup/rollup/blob/master/CHANGELOG.md#1120). 69 | 70 | 71 | 72 | # [3.4.0](https://github.com/evocateur/pectin/compare/v3.3.0...v3.4.0) (2019-08-01) 73 | 74 | 75 | ### Features 76 | 77 | * **deps:** Upgrade dependencies ([923f92f](https://github.com/evocateur/pectin/commit/923f92f)) 78 | 79 | 80 | 81 | 82 | 83 | # [3.3.0](https://github.com/evocateur/pectin/compare/v3.2.0...v3.3.0) (2019-06-17) 84 | 85 | **Note:** Version bump only for package @pectin/api 86 | 87 | 88 | 89 | 90 | 91 | # [3.2.0](https://github.com/evocateur/pectin/compare/v3.1.1...v3.2.0) (2019-05-03) 92 | 93 | **Note:** Version bump only for package @pectin/api 94 | 95 | 96 | 97 | 98 | 99 | # [3.1.0](https://github.com/evocateur/pectin/compare/v3.0.1...v3.1.0) (2019-04-09) 100 | 101 | **Note:** Version bump only for package @pectin/api 102 | 103 | 104 | 105 | 106 | 107 | ## [3.0.1](https://github.com/evocateur/pectin/compare/v3.0.0...v3.0.1) (2019-01-14) 108 | 109 | **Note:** Version bump only for package @pectin/api 110 | 111 | 112 | 113 | 114 | 115 | # [3.0.0](https://github.com/evocateur/pectin/compare/v2.6.0...v3.0.0) (2019-01-01) 116 | 117 | 118 | ### Features 119 | 120 | * **core:** Simplify API ([0c74c73](https://github.com/evocateur/pectin/commit/0c74c73)) 121 | 122 | 123 | ### BREAKING CHANGES 124 | 125 | * **core:** There is only a default export on pectin-core now, load your own package.json. 126 | 127 | 128 | 129 | 130 | 131 | # [2.6.0](https://github.com/evocateur/pectin/compare/v2.5.2...v2.6.0) (2018-12-27) 132 | 133 | 134 | ### Features 135 | 136 | * **core:** Enable chunking for CommonJS ([480f20d](https://github.com/evocateur/pectin/commit/480f20d)) 137 | 138 | 139 | 140 | 141 | 142 | ## [2.5.2](https://github.com/evocateur/pectin/compare/v2.5.1...v2.5.2) (2018-12-13) 143 | 144 | 145 | ### Bug Fixes 146 | 147 | * **api:** Build packages in topological order, dependencies before dependents ([50c35db](https://github.com/evocateur/pectin/commit/50c35db)) 148 | 149 | 150 | 151 | 152 | 153 | ## [2.5.1](https://github.com/evocateur/pectin/compare/v2.5.0...v2.5.1) (2018-11-16) 154 | 155 | **Note:** Version bump only for package @pectin/api 156 | 157 | 158 | 159 | 160 | 161 | # [2.5.0](https://github.com/evocateur/pectin/compare/v2.4.1...v2.5.0) (2018-11-15) 162 | 163 | **Note:** Version bump only for package @pectin/api 164 | 165 | 166 | 167 | 168 | 169 | ## [2.4.1](https://github.com/evocateur/pectin/compare/v2.4.0...v2.4.1) (2018-11-15) 170 | 171 | **Note:** Version bump only for package @pectin/api 172 | 173 | 174 | 175 | 176 | 177 | # [2.4.0](https://github.com/evocateur/pectin/compare/v2.3.0...v2.4.0) (2018-11-14) 178 | 179 | **Note:** Version bump only for package @pectin/api 180 | 181 | 182 | 183 | 184 | 185 | # [2.2.0](https://github.com/evocateur/pectin/compare/v2.1.2...v2.2.0) (2018-11-07) 186 | 187 | **Note:** Version bump only for package @pectin/api 188 | 189 | 190 | 191 | 192 | 193 | ## [2.1.2](https://github.com/evocateur/pectin/compare/v2.1.1...v2.1.2) (2018-10-19) 194 | 195 | **Note:** Version bump only for package @pectin/api 196 | 197 | 198 | 199 | 200 | 201 | ## [2.1.1](https://github.com/evocateur/pectin/compare/v2.1.0...v2.1.1) (2018-10-16) 202 | 203 | **Note:** Version bump only for package @pectin/api 204 | 205 | 206 | 207 | 208 | 209 | # [2.1.0](https://github.com/evocateur/pectin/compare/v2.0.0...v2.1.0) (2018-10-16) 210 | 211 | 212 | ### Features 213 | 214 | * **api:** Consume new plugins-per-format core API ([42a9659](https://github.com/evocateur/pectin/commit/42a9659)) 215 | * **api:** p-map ^2.0.0 ([574d9f1](https://github.com/evocateur/pectin/commit/574d9f1)) 216 | 217 | 218 | 219 | 220 | 221 | # [2.0.0](https://github.com/evocateur/pectin/compare/v1.3.0...v2.0.0) (2018-10-10) 222 | 223 | 224 | ### Features 225 | 226 | * Upgrade to Babel 7 ([#2](https://github.com/evocateur/pectin/issues/2)) ([3b460ba](https://github.com/evocateur/pectin/commit/3b460ba)), closes [#1](https://github.com/evocateur/pectin/issues/1) 227 | 228 | 229 | ### BREAKING CHANGES 230 | 231 | * Babel 6 is no longer supported. Consult https://babeljs.io/docs/en/v7-migration for upgrade steps. 232 | 233 | 234 | 235 | 236 | 237 | # [1.3.0](https://github.com/evocateur/pectin/compare/v1.2.0...v1.3.0) (2018-10-10) 238 | 239 | **Note:** Version bump only for package @pectin/api 240 | 241 | 242 | 243 | 244 | 245 | 246 | # [1.1.0](https://github.com/evocateur/pectin/compare/v1.0.0...v1.1.0) (2018-10-03) 247 | 248 | 249 | ### Features 250 | 251 | * **pectin-api:** Use [@lerna](https://github.com/lerna)/project static method to locate packages ([64f98e3](https://github.com/evocateur/pectin/commit/64f98e3)) 252 | 253 | 254 | 255 | 256 | 257 | 258 | # 1.0.0 (2018-08-31) 259 | 260 | 261 | ### Features 262 | 263 | * open source ([ce2d5cb](https://github.com/evocateur/pectin/commit/ce2d5cb)) 264 | -------------------------------------------------------------------------------- /packages/pectin-core/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [4.0.3](https://github.com/evocateur/pectin/compare/@pectin/core@4.0.2...@pectin/core@4.0.3) (2019-08-21) 7 | 8 | **Note:** Version bump only for package @pectin/core 9 | 10 | 11 | 12 | 13 | 14 | ## [4.0.2](https://github.com/evocateur/pectin/compare/@pectin/core@4.0.1...@pectin/core@4.0.2) (2019-08-06) 15 | 16 | 17 | ### Bug Fixes 18 | 19 | * **core:** The config object is _actually_ optional now ([cfdec36](https://github.com/evocateur/pectin/commit/cfdec36)) 20 | 21 | 22 | 23 | 24 | 25 | ## [4.0.1](https://github.com/evocateur/pectin/compare/@pectin/core@4.0.0...@pectin/core@4.0.1) (2019-08-02) 26 | 27 | **Note:** Version bump only for package @pectin/core 28 | 29 | 30 | 31 | 32 | 33 | # 4.0.0 (2019-08-01) 34 | 35 | 36 | ### Features 37 | 38 | * Upgrade `rollup-plugin-commonjs` & `rollup-plugin-node-resolve` ([979d7ec](https://github.com/evocateur/pectin/commit/979d7ec)) 39 | 40 | 41 | ### BREAKING CHANGES 42 | 43 | * The minimum version of the `rollup` peer dependency is now `^1.12.0`. 44 | 45 | The latest versions of `rollup-plugin-commonjs` ([changelog](https://github.com/rollup/rollup-plugin-commonjs/blob/master/CHANGELOG.md#1000)) and `rollup-plugin-node-resolve` ([changelog](https://github.com/rollup/rollup-plugin-node-resolve/blob/master/CHANGELOG.md#500-2019-05-15)) require core Rollup methods only available after [`rollup@v1.12.0`](https://github.com/rollup/rollup/blob/master/CHANGELOG.md#1120). 46 | 47 | 48 | 49 | # [3.4.0](https://github.com/evocateur/pectin/compare/v3.3.0...v3.4.0) (2019-08-01) 50 | 51 | 52 | ### Features 53 | 54 | * **deps:** Upgrade dependencies ([923f92f](https://github.com/evocateur/pectin/commit/923f92f)) 55 | 56 | 57 | 58 | 59 | 60 | # [3.3.0](https://github.com/evocateur/pectin/compare/v3.2.0...v3.3.0) (2019-06-17) 61 | 62 | 63 | ### Features 64 | 65 | * **core:** Interpolate `process.env.VERSION` with `pkg.version` ([0a0ac4e](https://github.com/evocateur/pectin/commit/0a0ac4e)) 66 | * Ssupport typescript extensions ([#8](https://github.com/evocateur/pectin/issues/8)) ([b0ee9f9](https://github.com/evocateur/pectin/commit/b0ee9f9)) 67 | 68 | 69 | 70 | 71 | 72 | # [3.2.0](https://github.com/evocateur/pectin/compare/v3.1.1...v3.2.0) (2019-05-03) 73 | 74 | **Note:** Version bump only for package @pectin/core 75 | 76 | 77 | 78 | 79 | 80 | # [3.1.0](https://github.com/evocateur/pectin/compare/v3.0.1...v3.1.0) (2019-04-09) 81 | 82 | 83 | ### Features 84 | 85 | * **core:** Migrate to new `mainFields` config for `rollup-plugin-node-resolve` ([f251e1f](https://github.com/evocateur/pectin/commit/f251e1f)) 86 | * **core:** Update dependencies ([e5901ca](https://github.com/evocateur/pectin/commit/e5901ca)) 87 | 88 | 89 | 90 | 91 | 92 | ## [3.0.1](https://github.com/evocateur/pectin/compare/v3.0.0...v3.0.1) (2019-01-14) 93 | 94 | 95 | ### Bug Fixes 96 | 97 | * **core:** Upgrade rollup-plugin-terser ([95de732](https://github.com/evocateur/pectin/commit/95de732)) 98 | 99 | 100 | 101 | 102 | 103 | # [3.0.0](https://github.com/evocateur/pectin/compare/v2.6.0...v3.0.0) (2019-01-01) 104 | 105 | 106 | ### Features 107 | 108 | * Rollup 1.0 🎉 ([938db4e](https://github.com/evocateur/pectin/commit/938db4e)) 109 | * **core:** Simplify API ([0c74c73](https://github.com/evocateur/pectin/commit/0c74c73)) 110 | 111 | 112 | ### BREAKING CHANGES 113 | 114 | * **core:** There is only a default export on pectin-core now, load your own package.json. 115 | * Rollup 0.x is no longer supported. 116 | 117 | 118 | 119 | 120 | 121 | # [2.6.0](https://github.com/evocateur/pectin/compare/v2.5.2...v2.6.0) (2018-12-27) 122 | 123 | 124 | ### Features 125 | 126 | * **cli:** Upgrade rollup to ^0.68.2 ([22a0411](https://github.com/evocateur/pectin/commit/22a0411)) 127 | * **core:** Enable chunking for CommonJS ([480f20d](https://github.com/evocateur/pectin/commit/480f20d)) 128 | 129 | 130 | 131 | 132 | 133 | ## [2.5.1](https://github.com/evocateur/pectin/compare/v2.5.0...v2.5.1) (2018-11-16) 134 | 135 | **Note:** Version bump only for package @pectin/core 136 | 137 | 138 | 139 | 140 | 141 | # [2.5.0](https://github.com/evocateur/pectin/compare/v2.4.1...v2.5.0) (2018-11-15) 142 | 143 | 144 | ### Features 145 | 146 | * **core:** Inject module global ponyfill ([3794d06](https://github.com/evocateur/pectin/commit/3794d06)) 147 | 148 | 149 | 150 | 151 | 152 | ## [2.4.1](https://github.com/evocateur/pectin/compare/v2.4.0...v2.4.1) (2018-11-15) 153 | 154 | **Note:** Version bump only for package @pectin/core 155 | 156 | 157 | 158 | 159 | 160 | # [2.4.0](https://github.com/evocateur/pectin/compare/v2.3.0...v2.4.0) (2018-11-14) 161 | 162 | **Note:** Version bump only for package @pectin/core 163 | 164 | 165 | 166 | 167 | 168 | # [2.2.0](https://github.com/evocateur/pectin/compare/v2.1.2...v2.2.0) (2018-11-07) 169 | 170 | 171 | ### Features 172 | 173 | * **core:** Enable code splitting for esm output ([#6](https://github.com/evocateur/pectin/issues/6)) ([1a0c369](https://github.com/evocateur/pectin/commit/1a0c369)), closes [#5](https://github.com/evocateur/pectin/issues/5) 174 | 175 | 176 | 177 | 178 | 179 | ## [2.1.2](https://github.com/evocateur/pectin/compare/v2.1.1...v2.1.2) (2018-10-19) 180 | 181 | **Note:** Version bump only for package @pectin/core 182 | 183 | 184 | 185 | 186 | 187 | ## [2.1.1](https://github.com/evocateur/pectin/compare/v2.1.0...v2.1.1) (2018-10-16) 188 | 189 | **Note:** Version bump only for package @pectin/core 190 | 191 | 192 | 193 | 194 | 195 | # [2.1.0](https://github.com/evocateur/pectin/compare/v2.0.0...v2.1.0) (2018-10-16) 196 | 197 | 198 | ### Bug Fixes 199 | 200 | * **core:** Make SVG inlining opt-in via package prop ([f072b17](https://github.com/evocateur/pectin/commit/f072b17)), closes [#4](https://github.com/evocateur/pectin/issues/4) 201 | * **core:** Re-order SVG plugin to avoid breaking React ([fc5202f](https://github.com/evocateur/pectin/commit/fc5202f)), closes [#4](https://github.com/evocateur/pectin/issues/4) 202 | 203 | 204 | ### Features 205 | 206 | * **core:** Add minified UMD output via pkg.unpkg with un-minified dev output ([e4e6f63](https://github.com/evocateur/pectin/commit/e4e6f63)) 207 | * **core:** Add replacement of NODE_ENV and BROWSER env vars ([236acd2](https://github.com/evocateur/pectin/commit/236acd2)) 208 | * **core:** Add simple and advanced browser output(s) via pkg.browser ([c8213d7](https://github.com/evocateur/pectin/commit/c8213d7)) 209 | * **core:** Generate plugins per-format instead of per-input ([4e81e6f](https://github.com/evocateur/pectin/commit/4e81e6f)) 210 | 211 | 212 | 213 | 214 | 215 | # [2.0.0](https://github.com/evocateur/pectin/compare/v1.3.0...v2.0.0) (2018-10-10) 216 | 217 | 218 | ### Features 219 | 220 | * Upgrade to Babel 7 ([#2](https://github.com/evocateur/pectin/issues/2)) ([3b460ba](https://github.com/evocateur/pectin/commit/3b460ba)), closes [#1](https://github.com/evocateur/pectin/issues/1) 221 | 222 | 223 | ### BREAKING CHANGES 224 | 225 | * Babel 6 is no longer supported. Consult https://babeljs.io/docs/en/v7-migration for upgrade steps. 226 | 227 | 228 | 229 | 230 | 231 | # [1.3.0](https://github.com/evocateur/pectin/compare/v1.2.0...v1.3.0) (2018-10-10) 232 | 233 | 234 | ### Features 235 | 236 | * **core:** Add rollup-plugin-svg ([#3](https://github.com/evocateur/pectin/issues/3)) ([92ce567](https://github.com/evocateur/pectin/commit/92ce567)) 237 | 238 | 239 | 240 | 241 | 242 | 243 | # 1.0.0 (2018-08-31) 244 | 245 | 246 | ### Features 247 | 248 | * open source ([ce2d5cb](https://github.com/evocateur/pectin/commit/ce2d5cb)) 249 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # NOTE 7 | 8 | Since v3.4.0, packages in this repository are versioned independently. 9 | 10 | Please consult the [releases](https://github.com/evocateur/pectin/releases) page to read subseqent changelogs. 11 | 12 | # [3.4.0](https://github.com/evocateur/pectin/compare/v3.3.0...v3.4.0) (2019-08-01) 13 | 14 | 15 | ### Features 16 | 17 | * **deps:** Upgrade dependencies ([923f92f](https://github.com/evocateur/pectin/commit/923f92f)) 18 | 19 | 20 | 21 | 22 | 23 | # [3.3.0](https://github.com/evocateur/pectin/compare/v3.2.0...v3.3.0) (2019-06-17) 24 | 25 | 26 | ### Features 27 | 28 | * **core:** Interpolate `process.env.VERSION` with `pkg.version` ([0a0ac4e](https://github.com/evocateur/pectin/commit/0a0ac4e)) 29 | * Ssupport typescript extensions ([#8](https://github.com/evocateur/pectin/issues/8)) ([b0ee9f9](https://github.com/evocateur/pectin/commit/b0ee9f9)) 30 | 31 | 32 | 33 | 34 | 35 | # [3.2.0](https://github.com/evocateur/pectin/compare/v3.1.1...v3.2.0) (2019-05-03) 36 | 37 | 38 | ### Bug Fixes 39 | 40 | * **main-entry:** Adapt to rollup v1.11.0 change to options.input default value ([232b9a8](https://github.com/evocateur/pectin/commit/232b9a8)) 41 | 42 | 43 | ### Features 44 | 45 | * **babelrc:** Recognize `@babel/runtime-corejs3` when injecting runtime transform ([43c749f](https://github.com/evocateur/pectin/commit/43c749f)) 46 | 47 | 48 | 49 | 50 | 51 | ## [3.1.1](https://github.com/evocateur/pectin/compare/v3.1.0...v3.1.1) (2019-04-09) 52 | 53 | 54 | ### Bug Fixes 55 | 56 | * **cli:** Rename homepage, too ([d89fc16](https://github.com/evocateur/pectin/commit/d89fc16)) 57 | 58 | 59 | 60 | 61 | 62 | # [3.1.0](https://github.com/evocateur/pectin/compare/v3.0.1...v3.1.0) (2019-04-09) 63 | 64 | 65 | ### Bug Fixes 66 | 67 | * **babelrc:** Update `cosmiconfig` dependency range to avoid audit warning ([ccfbd5d](https://github.com/evocateur/pectin/commit/ccfbd5d)) 68 | 69 | 70 | ### Features 71 | 72 | * **core:** Migrate to new `mainFields` config for `rollup-plugin-node-resolve` ([f251e1f](https://github.com/evocateur/pectin/commit/f251e1f)) 73 | * **core:** Update dependencies ([e5901ca](https://github.com/evocateur/pectin/commit/e5901ca)) 74 | * **pectin:** Rename CLI `@pectin/cli` -> `pectin` ([0a81cfe](https://github.com/evocateur/pectin/commit/0a81cfe)) 75 | 76 | 77 | 78 | 79 | 80 | ## [3.0.1](https://github.com/evocateur/pectin/compare/v3.0.0...v3.0.1) (2019-01-14) 81 | 82 | 83 | ### Bug Fixes 84 | 85 | * **core:** Upgrade rollup-plugin-terser ([95de732](https://github.com/evocateur/pectin/commit/95de732)) 86 | 87 | 88 | 89 | 90 | 91 | # [3.0.0](https://github.com/evocateur/pectin/compare/v2.6.0...v3.0.0) (2019-01-01) 92 | 93 | 94 | ### Features 95 | 96 | * Rollup 1.0 🎉 ([938db4e](https://github.com/evocateur/pectin/commit/938db4e)) 97 | * **core:** Simplify API ([0c74c73](https://github.com/evocateur/pectin/commit/0c74c73)) 98 | 99 | 100 | ### BREAKING CHANGES 101 | 102 | * **core:** There is only a default export on pectin-core now, load your own package.json. 103 | * Rollup 0.x is no longer supported. 104 | 105 | 106 | 107 | 108 | 109 | # [2.6.0](https://github.com/evocateur/pectin/compare/v2.5.2...v2.6.0) (2018-12-27) 110 | 111 | 112 | ### Features 113 | 114 | * **cli:** Upgrade rollup to ^0.68.2 ([22a0411](https://github.com/evocateur/pectin/commit/22a0411)) 115 | * **core:** Enable chunking for CommonJS ([480f20d](https://github.com/evocateur/pectin/commit/480f20d)) 116 | 117 | 118 | 119 | 120 | 121 | ## [2.5.2](https://github.com/evocateur/pectin/compare/v2.5.1...v2.5.2) (2018-12-13) 122 | 123 | 124 | ### Bug Fixes 125 | 126 | * **api:** Build packages in topological order, dependencies before dependents ([50c35db](https://github.com/evocateur/pectin/commit/50c35db)) 127 | 128 | 129 | 130 | 131 | 132 | ## [2.5.1](https://github.com/evocateur/pectin/compare/v2.5.0...v2.5.1) (2018-11-16) 133 | 134 | **Note:** Version bump only for package pectin-monorepo 135 | 136 | 137 | 138 | 139 | 140 | # [2.5.0](https://github.com/evocateur/pectin/compare/v2.4.1...v2.5.0) (2018-11-15) 141 | 142 | 143 | ### Features 144 | 145 | * **core:** Inject module global ponyfill ([3794d06](https://github.com/evocateur/pectin/commit/3794d06)) 146 | 147 | 148 | 149 | 150 | 151 | ## [2.4.1](https://github.com/evocateur/pectin/compare/v2.4.0...v2.4.1) (2018-11-15) 152 | 153 | **Note:** Version bump only for package pectin-monorepo 154 | 155 | 156 | 157 | 158 | 159 | # [2.4.0](https://github.com/evocateur/pectin/compare/v2.3.0...v2.4.0) (2018-11-14) 160 | 161 | 162 | ### Features 163 | 164 | * **subpath-externals:** Accept package props for fine-grained control ([b4f134b](https://github.com/evocateur/pectin/commit/b4f134b)) 165 | 166 | 167 | 168 | 169 | 170 | # [2.3.0](https://github.com/evocateur/pectin/compare/v2.2.0...v2.3.0) (2018-11-13) 171 | 172 | 173 | ### Features 174 | 175 | * **rollup-config-pectin:** Use advanced multi-config for better ESM output ([92be659](https://github.com/evocateur/pectin/commit/92be659)) 176 | 177 | 178 | 179 | 180 | 181 | # [2.2.0](https://github.com/evocateur/pectin/compare/v2.1.2...v2.2.0) (2018-11-07) 182 | 183 | 184 | ### Features 185 | 186 | * **babelrc:** Ensure dynamic `import()` syntax is enabled for ESM format ([44626ad](https://github.com/evocateur/pectin/commit/44626ad)) 187 | * **cli:** Upgrade rollup to ^0.67.0 ([11f3dba](https://github.com/evocateur/pectin/commit/11f3dba)) 188 | * **core:** Enable code splitting for esm output ([#6](https://github.com/evocateur/pectin/issues/6)) ([1a0c369](https://github.com/evocateur/pectin/commit/1a0c369)), closes [#5](https://github.com/evocateur/pectin/issues/5) 189 | 190 | 191 | 192 | 193 | 194 | ## [2.1.2](https://github.com/evocateur/pectin/compare/v2.1.1...v2.1.2) (2018-10-19) 195 | 196 | 197 | ### Bug Fixes 198 | 199 | * **babelrc:** Handle [@babel](https://github.com/babel)/runtime-corejs2 dependency correctly ([ffc9f19](https://github.com/evocateur/pectin/commit/ffc9f19)) 200 | 201 | 202 | 203 | 204 | 205 | ## [2.1.1](https://github.com/evocateur/pectin/compare/v2.1.0...v2.1.1) (2018-10-16) 206 | 207 | 208 | ### Bug Fixes 209 | 210 | * **babelrc:** Do not duplicate existing runtime transform ([aab8e4e](https://github.com/evocateur/pectin/commit/aab8e4e)) 211 | 212 | 213 | 214 | 215 | 216 | # [2.1.0](https://github.com/evocateur/pectin/compare/v2.0.0...v2.1.0) (2018-10-16) 217 | 218 | 219 | ### Bug Fixes 220 | 221 | * **core:** Make SVG inlining opt-in via package prop ([f072b17](https://github.com/evocateur/pectin/commit/f072b17)), closes [#4](https://github.com/evocateur/pectin/issues/4) 222 | * **core:** Re-order SVG plugin to avoid breaking React ([fc5202f](https://github.com/evocateur/pectin/commit/fc5202f)), closes [#4](https://github.com/evocateur/pectin/issues/4) 223 | 224 | 225 | ### Features 226 | 227 | * **api:** Consume new plugins-per-format core API ([42a9659](https://github.com/evocateur/pectin/commit/42a9659)) 228 | * **api:** p-map ^2.0.0 ([574d9f1](https://github.com/evocateur/pectin/commit/574d9f1)) 229 | * **babelrc:** Accept optional format config that controls value of useESModules option passed to runtime transform ([8e7622d](https://github.com/evocateur/pectin/commit/8e7622d)) 230 | * **core:** Add minified UMD output via pkg.unpkg with un-minified dev output ([e4e6f63](https://github.com/evocateur/pectin/commit/e4e6f63)) 231 | * **core:** Add replacement of NODE_ENV and BROWSER env vars ([236acd2](https://github.com/evocateur/pectin/commit/236acd2)) 232 | * **core:** Add simple and advanced browser output(s) via pkg.browser ([c8213d7](https://github.com/evocateur/pectin/commit/c8213d7)) 233 | * **core:** Generate plugins per-format instead of per-input ([4e81e6f](https://github.com/evocateur/pectin/commit/4e81e6f)) 234 | * **subpath-externals:** Accept optional format config that controls which types of dependencies are externalized ([446440d](https://github.com/evocateur/pectin/commit/446440d)) 235 | 236 | 237 | 238 | 239 | 240 | # [2.0.0](https://github.com/evocateur/pectin/compare/v1.3.0...v2.0.0) (2018-10-10) 241 | 242 | 243 | ### Features 244 | 245 | * Upgrade to Babel 7 ([#2](https://github.com/evocateur/pectin/issues/2)) ([3b460ba](https://github.com/evocateur/pectin/commit/3b460ba)), closes [#1](https://github.com/evocateur/pectin/issues/1) 246 | 247 | 248 | ### BREAKING CHANGES 249 | 250 | * Babel 6 is no longer supported. Consult https://babeljs.io/docs/en/v7-migration for upgrade steps. 251 | 252 | 253 | 254 | 255 | 256 | # [1.3.0](https://github.com/evocateur/pectin/compare/v1.2.0...v1.3.0) (2018-10-10) 257 | 258 | 259 | ### Features 260 | 261 | * **core:** Add rollup-plugin-svg ([#3](https://github.com/evocateur/pectin/issues/3)) ([92ce567](https://github.com/evocateur/pectin/commit/92ce567)) 262 | 263 | 264 | 265 | 266 | 267 | 268 | # [1.2.0](https://github.com/evocateur/pectin/compare/v1.1.0...v1.2.0) (2018-10-03) 269 | 270 | 271 | ### Features 272 | 273 | * **cli:** Upgrade rollup to ^0.66.3 ([653ed9b](https://github.com/evocateur/pectin/commit/653ed9b)) 274 | 275 | 276 | 277 | 278 | 279 | 280 | # [1.1.0](https://github.com/evocateur/pectin/compare/v1.0.0...v1.1.0) (2018-10-03) 281 | 282 | 283 | ### Features 284 | 285 | * **pectin-api:** Use [@lerna](https://github.com/lerna)/project static method to locate packages ([64f98e3](https://github.com/evocateur/pectin/commit/64f98e3)) 286 | 287 | 288 | 289 | 290 | 291 | 292 | # 1.0.0 (2018-08-31) 293 | 294 | 295 | ### Features 296 | 297 | * open source ([ce2d5cb](https://github.com/evocateur/pectin/commit/ce2d5cb)) 298 | -------------------------------------------------------------------------------- /packages/rollup-plugin-subpath-externals/test/rollup-plugin-subpath-externals.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | rollup, 3 | Plugin, 4 | InputOptions, 5 | OutputOptions, 6 | RollupBuild, 7 | OutputChunk, 8 | PreRenderedChunk, 9 | } from 'rollup'; 10 | import subpathExternals from '../lib/rollup-plugin-subpath-externals'; 11 | 12 | function stubFile(fileName: string, fileContent: string): Plugin { 13 | return { 14 | name: 'stub-file', 15 | resolveId: (id): string => { 16 | if (id === fileName) { 17 | return fileName; 18 | } 19 | 20 | return null; 21 | }, 22 | load: (id): string => { 23 | if (id === fileName) { 24 | return fileContent; 25 | } 26 | 27 | return null; 28 | }, 29 | }; 30 | } 31 | 32 | function stubInput(fileContent: string): Plugin { 33 | return { 34 | ...stubFile('stub.js', fileContent), 35 | name: 'stub-input', 36 | options: (opts: InputOptions): InputOptions => { 37 | // eslint-disable-next-line no-param-reassign 38 | opts.input = 'stub.js'; 39 | 40 | return opts; 41 | }, 42 | }; 43 | } 44 | 45 | async function getEntryChunk(bundle: RollupBuild, config?: OutputOptions): Promise { 46 | const { output } = await bundle.generate(config || { format: 'esm' }); 47 | 48 | // jesus this is convoluted. apparently interface extension only works for one hop? 49 | return (output as OutputChunk[]).find(chunk => (chunk as PreRenderedChunk).isEntry); 50 | } 51 | 52 | describe('rollup-plugin-subpath-externals', () => { 53 | it('overwrites existing opts.external', async () => { 54 | const bundle = await rollup({ 55 | external: importee => !!`overwritten-${importee}`, 56 | plugins: [ 57 | stubInput(` 58 | import trim from 'lodash/trim'; 59 | export default (s) => trim(s); 60 | `), 61 | subpathExternals({ 62 | dependencies: { lodash: '*' }, 63 | }), 64 | ], 65 | }); 66 | const chunk = await getEntryChunk(bundle); 67 | 68 | expect(chunk.imports).toStrictEqual(['lodash/trim']); 69 | }); 70 | 71 | it('ignores local imports', async () => { 72 | const bundle = await rollup({ 73 | plugins: [ 74 | stubInput(` 75 | import local from './local'; 76 | export default (s) => local(s); 77 | `), 78 | stubFile('./local', 'export default function local() {}'), 79 | subpathExternals({ 80 | dependencies: { somelib: '*' }, 81 | }), 82 | ], 83 | }); 84 | const chunk = await getEntryChunk(bundle); 85 | 86 | expect(chunk.imports).toStrictEqual([]); 87 | }); 88 | 89 | it('ignores devDependencies', async () => { 90 | const bundle = await rollup({ 91 | plugins: [ 92 | stubFile('foo', 'export default function foo() {};'), 93 | stubInput(` 94 | import foo from 'foo'; 95 | export default (s) => foo(s); 96 | `), 97 | subpathExternals({ 98 | devDependencies: { foo: '*' }, 99 | }), 100 | ], 101 | }); 102 | const chunk = await getEntryChunk(bundle); 103 | 104 | expect(chunk.imports).toStrictEqual([]); 105 | }); 106 | 107 | it('externalizes exact matches', async () => { 108 | const bundle = await rollup({ 109 | plugins: [ 110 | stubInput(` 111 | import { trim } from 'lodash'; 112 | export default (s) => trim(s); 113 | `), 114 | subpathExternals({ 115 | dependencies: { lodash: '*' }, 116 | }), 117 | ], 118 | }); 119 | 120 | const chunk = await getEntryChunk(bundle); 121 | 122 | expect(chunk.imports).toStrictEqual(['lodash']); 123 | }); 124 | 125 | it('externalizes subpath imports', async () => { 126 | const bundle = await rollup({ 127 | plugins: [ 128 | stubInput(` 129 | import trim from 'lodash/trim'; 130 | export default (s) => trim(s); 131 | `), 132 | subpathExternals({ 133 | dependencies: { lodash: '*' }, 134 | }), 135 | ], 136 | }); 137 | 138 | const chunk = await getEntryChunk(bundle); 139 | 140 | expect(chunk.imports).toStrictEqual(['lodash/trim']); 141 | }); 142 | 143 | it('externalizes peerDependencies', async () => { 144 | const bundle = await rollup({ 145 | plugins: [ 146 | stubInput(` 147 | import React from 'react'; 148 | export default (s) => React.render(s); 149 | `), 150 | subpathExternals({ 151 | devDependencies: { 152 | enzyme: '*', 153 | react: '*', 154 | }, 155 | peerDependencies: { 156 | react: '^15.0.0 || ^16.0.0', 157 | }, 158 | }), 159 | ], 160 | }); 161 | 162 | const chunk = await getEntryChunk(bundle); 163 | 164 | expect(chunk.imports).toStrictEqual(['react']); 165 | }); 166 | 167 | it('externalizes _only_ peerDependencies when output.format is "umd"', async () => { 168 | const bundle = await rollup({ 169 | plugins: [ 170 | { 171 | name: 'stub-whackadoodle', 172 | resolveId(id): string { 173 | if (id === 'whackadoodle') { 174 | return 'whackadoodle'; 175 | } 176 | 177 | return null; 178 | }, 179 | load(id): string { 180 | if (id === 'whackadoodle') { 181 | return 'export default function whackaDoodle(props) { return props.children; };'; 182 | } 183 | 184 | return null; 185 | }, 186 | }, 187 | stubInput(` 188 | import React from 'react'; 189 | import woo from 'whackadoodle'; 190 | export default (s) => React.render(woo(s)); 191 | `), 192 | subpathExternals( 193 | { 194 | dependencies: { 195 | whackadoodle: '*', 196 | }, 197 | devDependencies: { 198 | react: '*', 199 | }, 200 | peerDependencies: { 201 | react: '^15.0.0 || ^16.0.0', 202 | }, 203 | }, 204 | { format: 'umd' } 205 | ), 206 | ], 207 | }); 208 | 209 | const { code } = await getEntryChunk(bundle, { 210 | format: 'umd', 211 | name: 'StubComponent', 212 | globals: { 213 | react: 'React', 214 | }, 215 | // eslint-disable-next-line @typescript-eslint/ban-ts-ignore 216 | // @ts-ignore (it bloody well works, mr. typescript) 217 | indent: ' ', 218 | }); 219 | 220 | expect(code).toMatchInlineSnapshot(` 221 | "(function (global, factory) { 222 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('react')) : 223 | typeof define === 'function' && define.amd ? define(['react'], factory) : 224 | (global = global || self, global.StubComponent = factory(global.React)); 225 | }(this, (function (React) { 'use strict'; 226 | 227 | React = React && React.hasOwnProperty('default') ? React['default'] : React; 228 | 229 | function whackaDoodle(props) { return props.children; } 230 | 231 | var stub = (s) => React.render(whackaDoodle(s)); 232 | 233 | return stub; 234 | 235 | }))); 236 | " 237 | `); 238 | }); 239 | 240 | it('externalizes node built-ins', async () => { 241 | const bundle = await rollup({ 242 | plugins: [ 243 | stubInput(` 244 | import url from 'url'; 245 | import qs from 'querystring'; 246 | export default (s) => url.join(s, qs.stringify({ s })); 247 | `), 248 | subpathExternals({}), 249 | ], 250 | }); 251 | 252 | const chunk = await getEntryChunk(bundle); 253 | 254 | expect(chunk.imports).toStrictEqual(['url', 'querystring']); 255 | }); 256 | 257 | it('accepts explicit external config via package prop', async () => { 258 | const bundle = await rollup({ 259 | plugins: [ 260 | { 261 | name: 'stub-embedded', 262 | resolveId(id): string { 263 | if (id === 'embedded') { 264 | return 'embedded'; 265 | } 266 | 267 | return null; 268 | }, 269 | load(id): string { 270 | if (id === 'embedded') { 271 | return 'export default function embedded(k) { return k; };'; 272 | } 273 | 274 | return null; 275 | }, 276 | }, 277 | stubInput(` 278 | import embedded from 'embedded'; 279 | import trim from 'lodash/trim'; 280 | export default (s) => embedded(trim(s)); 281 | `), 282 | subpathExternals({ 283 | rollup: { 284 | external: ['lodash'], 285 | }, 286 | dependencies: { 287 | embedded: 'test', 288 | lodash: '*', 289 | }, 290 | }), 291 | ], 292 | }); 293 | 294 | const chunk = await getEntryChunk(bundle); 295 | 296 | expect(chunk.imports).toStrictEqual(['lodash/trim']); 297 | 298 | expect(chunk.code).toMatchInlineSnapshot(` 299 | "import trim from 'lodash/trim'; 300 | 301 | function embedded(k) { return k; } 302 | 303 | var stub = (s) => embedded(trim(s)); 304 | 305 | export default stub; 306 | " 307 | `); 308 | }); 309 | 310 | it('accepts explicit bundle config via package prop', async () => { 311 | const bundle = await rollup({ 312 | plugins: [ 313 | { 314 | name: 'stub-inlined', 315 | resolveId(id): string { 316 | if (id === 'inlined') { 317 | return 'inlined'; 318 | } 319 | 320 | return null; 321 | }, 322 | load(id): string { 323 | if (id === 'inlined') { 324 | return 'export default function inlined(k) { return k; };'; 325 | } 326 | 327 | return null; 328 | }, 329 | }, 330 | stubInput(` 331 | import inlined from 'inlined'; 332 | import trim from 'lodash/trim'; 333 | export default (s) => inlined(trim(s)); 334 | `), 335 | subpathExternals({ 336 | rollup: { 337 | bundle: ['inlined'], 338 | }, 339 | dependencies: { 340 | inlined: 'test', 341 | lodash: '*', 342 | }, 343 | }), 344 | ], 345 | }); 346 | 347 | const chunk = await getEntryChunk(bundle); 348 | 349 | expect(chunk.imports).toStrictEqual(['lodash/trim']); 350 | 351 | expect(chunk.code).toMatchInlineSnapshot(` 352 | "import trim from 'lodash/trim'; 353 | 354 | function inlined(k) { return k; } 355 | 356 | var stub = (s) => inlined(trim(s)); 357 | 358 | export default stub; 359 | " 360 | `); 361 | }); 362 | 363 | it('works all together', async () => { 364 | const bundle = await rollup({ 365 | plugins: [ 366 | stubInput(` 367 | import url from 'url'; 368 | import get from 'lodash/get'; 369 | import peer from 'a-peer-dependency'; 370 | export default function stub() { 371 | return peer(url.resolve('/', get(opts, 'path'))); 372 | } 373 | `), 374 | subpathExternals({ 375 | peerDependencies: { 376 | 'a-peer-dependency': '*', 377 | }, 378 | dependencies: { 379 | lodash: '*', 380 | }, 381 | }), 382 | ], 383 | }); 384 | 385 | const chunk = await getEntryChunk(bundle); 386 | 387 | expect(chunk.imports).toStrictEqual(['url', 'lodash/get', 'a-peer-dependency']); 388 | }); 389 | }); 390 | -------------------------------------------------------------------------------- /packages/pectin-api/test/pectin-api.test.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import Tacks = require('tacks'); 3 | import tempy = require('tempy'); 4 | import touch = require('touch'); 5 | import { findConfigs, generateConfig, isUpToDate } from '../lib/pectin-api'; 6 | 7 | const { Dir, File, Symlink } = Tacks; 8 | 9 | type UpdateHelper = { 10 | (fp: string): Promise; 11 | cwd: string; 12 | }; 13 | 14 | const makeUpdater = (cwd: string): UpdateHelper => { 15 | const ctime = Date.now() / 1000; 16 | // eslint-disable-next-line @typescript-eslint/ban-ts-ignore 17 | // @ts-ignore (the types are wrong, stuff it mr. typescript) 18 | const opts: touch.Options = { mtime: ctime + 1 }; 19 | const updater = async (fp: string): Promise => touch(path.join(cwd, 'modules', fp), opts); 20 | 21 | // avoid process.cwd() calls 22 | updater.cwd = cwd; 23 | 24 | return updater; 25 | }; 26 | 27 | type TacksItem = Tacks.Dir | Tacks.File | Tacks.Symlink; 28 | 29 | function createFixture(pkgSpec: { [fp: string]: TacksItem }): UpdateHelper { 30 | const cwd = tempy.directory(); 31 | const fixture = new Tacks( 32 | Dir({ 33 | '.babelrc': File({ 34 | presets: ['@babel/preset-env'], 35 | }), 36 | 'lerna.json': File({ 37 | packages: ['modules/**'], 38 | }), 39 | 'package.json': File({ 40 | name: 'monorepo', 41 | private: true, 42 | }), 43 | 'modules': Dir(pkgSpec), 44 | }) 45 | ); 46 | 47 | fixture.create(cwd); 48 | process.chdir(cwd); 49 | 50 | return makeUpdater(cwd); 51 | } 52 | 53 | describe('pectin-api', () => { 54 | // avoid polluting other test state 55 | const REPO_ROOT = path.resolve(__dirname, '../../..'); 56 | 57 | afterAll(() => { 58 | process.chdir(REPO_ROOT); 59 | }); 60 | 61 | afterEach(() => { 62 | jest.restoreAllMocks(); 63 | delete process.env.ROLLUP_WATCH; 64 | }); 65 | 66 | it('builds packages when output directory is missing', async () => { 67 | createFixture({ 68 | 'no-output': Dir({ 69 | 'package.json': File({ 70 | name: '@test/no-output', 71 | main: 'dist/index.js', 72 | }), 73 | 'src': Dir({ 74 | 'index.js': File('export default "test";'), 75 | }), 76 | }), 77 | }); 78 | 79 | await expect(findConfigs()).resolves.toMatchObject([ 80 | { input: 'modules/no-output/src/index.js' }, 81 | ]); 82 | }); 83 | 84 | it('builds packages in topological order', async () => { 85 | createFixture({ 86 | 'a-dependent': Dir({ 87 | 'package.json': File({ 88 | name: '@test/a-dependent', 89 | main: 'dist/index.js', 90 | dependencies: { 91 | '@test/their-dependency': 'file:../their-dependency', 92 | }, 93 | }), 94 | 'src': Dir({ 95 | 'index.js': File('export default "test";'), 96 | }), 97 | }), 98 | 'their-dependency': Dir({ 99 | 'package.json': File({ 100 | name: '@test/their-dependency', 101 | main: 'dist/index.js', 102 | }), 103 | 'src': Dir({ 104 | 'index.js': File('export default "test";'), 105 | }), 106 | }), 107 | }); 108 | 109 | await expect(findConfigs()).resolves.toMatchObject([ 110 | { input: 'modules/their-dependency/src/index.js' }, 111 | { input: 'modules/a-dependent/src/index.js' }, 112 | ]); 113 | }); 114 | 115 | it('builds packages when input files are newer than output', async () => { 116 | const updateFile = createFixture({ 117 | 'old-output': Dir({ 118 | 'package.json': File({ 119 | name: '@test/old-output', 120 | main: 'lib/index.js', 121 | }), 122 | 'lib': Dir({ 123 | 'index.js': File('module.exports = "test";'), 124 | }), 125 | 'src': Dir({ 126 | 'index.js': File('export default from "./other";'), 127 | 'other.js': File('export default "test";'), 128 | }), 129 | }), 130 | }); 131 | 132 | await updateFile('old-output/src/other.js'); 133 | 134 | await expect(findConfigs()).resolves.toMatchObject([ 135 | { input: 'modules/old-output/src/index.js' }, 136 | ]); 137 | }); 138 | 139 | it('matches .jsx files, too', async () => { 140 | const updateFile = createFixture({ 141 | 'jsx-input': Dir({ 142 | 'package.json': File({ 143 | name: '@test/jsx-input', 144 | main: 'dist/index.js', 145 | }), 146 | 'dist': Dir({ 147 | 'index.js': File('module.exports = "test";'), 148 | }), 149 | 'src': Dir({ 150 | 'index.js': File('export default from "./other";'), 151 | 'other.jsx': File('export default "test";'), 152 | }), 153 | }), 154 | }); 155 | 156 | await updateFile('jsx-input/src/other.jsx'); 157 | 158 | await expect(findConfigs()).resolves.toMatchObject([ 159 | { input: 'modules/jsx-input/src/index.js' }, 160 | ]); 161 | }); 162 | 163 | it('does not build when input files are older than output', async () => { 164 | const updateFile = createFixture({ 165 | 'old-input': Dir({ 166 | 'package.json': File({ 167 | name: '@test/old-input', 168 | main: 'lib/index.js', 169 | }), 170 | 'lib': Dir({ 171 | 'index.js': File('module.exports = "test";'), 172 | }), 173 | 'src': Dir({ 174 | 'index.js': File('export default from "./other";'), 175 | 'other.js': File('export default "test";'), 176 | }), 177 | }), 178 | }); 179 | 180 | await Promise.all([ 181 | updateFile('old-input/lib/index.js'), 182 | // less than OR equal 183 | updateFile('old-input/src/other.js'), 184 | ]); 185 | 186 | await expect(findConfigs()).resolves.toStrictEqual([]); 187 | }); 188 | 189 | it('does not compare build output with itself', async () => { 190 | const updateFile = createFixture({ 191 | 'rooted-input': Dir({ 192 | 'package.json': File({ 193 | name: '@test/rooted-input', 194 | main: 'dist/index.js', 195 | rollup: { 196 | input: 'app.js', 197 | }, 198 | }), 199 | 'app.js': File('export default "test";'), 200 | 'dist': Dir({ 201 | 'index.js': File('module.exports = "test";'), 202 | }), 203 | }), 204 | }); 205 | 206 | await updateFile('rooted-input/dist/index.js'); 207 | 208 | await expect(findConfigs()).resolves.toStrictEqual([]); 209 | }); 210 | 211 | it('does not compare tests or node_modules with last build', async () => { 212 | const updateFile = createFixture({ 213 | 'rooted-ignore': Dir({ 214 | 'package.json': File({ 215 | name: '@test/rooted-ignore', 216 | main: 'dist/index.js', 217 | rollup: { 218 | input: 'app.js', 219 | }, 220 | }), 221 | 'app.js': File('export default "test";'), 222 | '__tests__': Dir({ 223 | 'ignored.js': File('ignored'), 224 | }), 225 | 'dist': Dir({ 226 | 'index.js': File('module.exports = "test";'), 227 | }), 228 | 'node_modules': Dir({ 229 | foo: Dir({ 230 | 'index.js': File('ignored'), 231 | 'package.json': File({ 232 | name: 'foo', 233 | main: 'index.js', 234 | }), 235 | }), 236 | }), 237 | 'src': Dir({ 238 | '__tests__': Dir({ 239 | 'ignored.js': File('ignored'), 240 | }), 241 | 'ignored-test.js': File('ignored'), 242 | 'ignored.test.js': File('ignored'), 243 | }), 244 | 'test': Dir({ 245 | 'ignored.js': File('ignored'), 246 | }), 247 | }), 248 | }); 249 | 250 | await Promise.all([ 251 | updateFile('rooted-ignore/__tests__/ignored.js'), 252 | updateFile('rooted-ignore/node_modules/foo/index.js'), 253 | updateFile('rooted-ignore/src/__tests__/ignored.js'), 254 | updateFile('rooted-ignore/src/ignored-test.js'), 255 | updateFile('rooted-ignore/src/ignored.test.js'), 256 | updateFile('rooted-ignore/test/ignored.js'), 257 | ]); 258 | 259 | await expect(findConfigs()).resolves.toStrictEqual([]); 260 | }); 261 | 262 | it('does not watch a module with pkg.rollup.ignoreWatch', async () => { 263 | const updateFile = createFixture({ 264 | unwatched: Dir({ 265 | 'package.json': File({ 266 | name: '@test/unwatched', 267 | main: 'dist/index.js', 268 | rollup: { 269 | ignoreWatch: true, 270 | }, 271 | }), 272 | 'dist': Dir({ 273 | 'index.js': File('module.exports = "test";'), 274 | }), 275 | 'src': Dir({ 276 | 'index.js': File('export default "test";'), 277 | }), 278 | }), 279 | }); 280 | 281 | await updateFile('unwatched/src/index.js'); 282 | 283 | // simulate `rollup --watch` 284 | process.env.ROLLUP_WATCH = 'true'; 285 | 286 | await expect(findConfigs()).resolves.toStrictEqual([]); 287 | }); 288 | 289 | it('does not build a module with pkg.rollup.skip', async () => { 290 | createFixture({ 291 | skipped: Dir({ 292 | 'package.json': File({ 293 | name: '@test/skipped', 294 | main: 'dist/index.js', 295 | rollup: { 296 | skip: true, 297 | }, 298 | }), 299 | 'lib': Dir({ 300 | 'index.js': File('module.exports = "test";'), 301 | }), 302 | }), 303 | }); 304 | 305 | await expect(findConfigs()).resolves.toStrictEqual([]); 306 | }); 307 | 308 | it('does not build a module with missing pkg.main', async () => { 309 | jest.spyOn(console, 'error').mockImplementation(() => { 310 | /* avoid console spam when error is logged */ 311 | }); 312 | 313 | createFixture({ 314 | 'no-pkg-main': Dir({ 315 | 'package.json': File({ 316 | name: '@test/no-pkg-main', 317 | }), 318 | 'lib': Dir({ 319 | 'index.js': File('module.exports = "test";'), 320 | }), 321 | }), 322 | }); 323 | 324 | await expect(findConfigs()).resolves.toStrictEqual([]); 325 | 326 | // eslint-disable-next-line no-console 327 | expect(console.error).toHaveBeenCalled(); 328 | }); 329 | 330 | it('uses cwd argument instead of implicit process.cwd()', async () => { 331 | const { cwd } = createFixture({ 332 | 'explicit-cwd': Dir({ 333 | 'package.json': File({ 334 | name: '@test/explicit-cwd', 335 | main: 'dist/index.js', 336 | }), 337 | 'src': Dir({ 338 | 'index.js': File('export default "test";'), 339 | }), 340 | }), 341 | }); 342 | 343 | // change implicit process.cwd() 344 | process.chdir(REPO_ROOT); 345 | 346 | await expect(findConfigs({ cwd })).resolves.toMatchObject([ 347 | { 348 | input: path.relative('.', path.join(cwd, 'modules/explicit-cwd/src/index.js')), 349 | }, 350 | ]); 351 | }); 352 | 353 | it('supports recursive package globs', async () => { 354 | const updateFile = createFixture({ 355 | app: Dir({ 356 | 'package.json': File({ 357 | name: '@test/app', 358 | main: 'dist/index.js', 359 | dependencies: { 360 | 'missing-dist': '../lib/missing-dist', 361 | }, 362 | }), 363 | 'dist': Dir({ 364 | 'index.js': File('module.exports = "test";'), 365 | }), 366 | 'node_modules': Dir({ 367 | 'missing-dist': Symlink('../../lib/missing-dist'), 368 | }), 369 | 'src': Dir({ 370 | 'index.js': File('export default from "./other";'), 371 | 'other.js': File('export default "test";'), 372 | }), 373 | }), 374 | lib: Dir({ 375 | 'missing-dist': Dir({ 376 | 'package.json': File({ 377 | name: '@test/missing-dist', 378 | main: 'dist/index.js', 379 | module: 'dist/index.module.js', 380 | dependencies: { 381 | bar: '^1.0.0', 382 | }, 383 | }), 384 | 'node_modules': Dir({ 385 | bar: Dir({ 386 | 'lib': Dir({ 387 | 'index.js': File('ignored'), 388 | }), 389 | 'package.json': File({ 390 | name: 'bar', 391 | main: 'lib/index.js', 392 | }), 393 | 'src': Dir({ 394 | 'index.js': File('do not transpile node_modules :P'), 395 | }), 396 | }), 397 | }), 398 | 'src': Dir({ 399 | 'index.js': File('export default "test";'), 400 | }), 401 | }), 402 | }), 403 | }); 404 | 405 | await Promise.all([ 406 | updateFile('app/src/other.js'), 407 | updateFile('lib/missing-dist/node_modules/bar/src/index.js'), 408 | ]); 409 | 410 | await expect(findConfigs()).resolves.toMatchObject([ 411 | { 412 | input: 'modules/lib/missing-dist/src/index.js', 413 | output: [{ format: 'cjs', exports: 'auto' }], 414 | }, 415 | { 416 | input: 'modules/lib/missing-dist/src/index.js', 417 | output: [{ format: 'esm', exports: 'named' }], 418 | }, 419 | { 420 | input: 'modules/app/src/index.js', 421 | output: [{ format: 'cjs', exports: 'auto' }], 422 | }, 423 | ]); 424 | }); 425 | 426 | it('sets watch config for all inputs when enabled', async () => { 427 | const updateFile = createFixture({ 428 | 'watch-existing': Dir({ 429 | 'package.json': File({ 430 | name: '@test/watch-existing', 431 | main: 'lib/index.js', 432 | module: 'lib/index.module.js', 433 | }), 434 | 'lib': Dir({ 435 | 'index.js': File('module.exports = "test";'), 436 | }), 437 | 'src': Dir({ 438 | 'index.js': File('export default from "./other";'), 439 | 'other.js': File('export default "test";'), 440 | }), 441 | }), 442 | 'watch-missing': Dir({ 443 | 'package.json': File({ 444 | name: '@test/watch-missing', 445 | main: 'lib/index.js', 446 | }), 447 | 'src': Dir({ 448 | 'index.js': File('export default "unbuilt";'), 449 | }), 450 | }), 451 | }); 452 | 453 | await Promise.all([ 454 | updateFile('watch-existing/lib/index.js'), 455 | // watch always builds _everything_ 456 | updateFile('watch-existing/src/other.js'), 457 | ]); 458 | 459 | await expect(findConfigs({ watch: true })).resolves.toMatchObject([ 460 | { watch: { clearScreen: false } }, 461 | { watch: { clearScreen: false } }, 462 | { watch: { clearScreen: false } }, 463 | ]); 464 | }); 465 | 466 | describe('generateConfig', () => { 467 | it('supports 1.x cwd config location', async () => { 468 | const { cwd } = createFixture({ 469 | 'package.json': File({ 470 | name: '@test/pkg-cwd', 471 | main: 'dist/index.js', 472 | }), 473 | 'src': Dir({ 474 | 'index.js': File('export default "test";'), 475 | }), 476 | }); 477 | const pkg = { 478 | name: '@test/pkg-cwd', 479 | main: 'dist/index.js', 480 | cwd, 481 | }; 482 | const opts = {}; 483 | const config = await generateConfig(pkg, opts); 484 | 485 | expect(config).toMatchObject([ 486 | { 487 | input: 'src/index.js', 488 | output: [{ format: 'cjs', exports: 'auto' }], 489 | }, 490 | ]); 491 | // options are not mutated 492 | expect(opts).toStrictEqual({}); 493 | }); 494 | }); 495 | 496 | describe('isUpToDate', () => { 497 | it('supports 1.x argument signature', async () => { 498 | const updateFile = createFixture({ 499 | 'package.json': File({ 500 | name: '@test/up-to-date', 501 | main: 'dist/index.js', 502 | }), 503 | 'dist': Dir({ 504 | 'index.js': File('module.exports = "test";'), 505 | }), 506 | 'src': Dir({ 507 | 'index.js': File('export default "test";'), 508 | }), 509 | }); 510 | const { cwd } = updateFile; 511 | 512 | await updateFile('src/index.js'); 513 | 514 | const result = await isUpToDate( 515 | { cwd }, 516 | { 517 | input: 'src/index.js', 518 | output: [ 519 | { 520 | file: path.resolve(cwd, 'dist/index.js'), 521 | format: 'cjs', 522 | exports: 'auto', 523 | }, 524 | ], 525 | } 526 | ); 527 | 528 | expect(result).toBe(false); 529 | }); 530 | }); 531 | }); 532 | -------------------------------------------------------------------------------- /packages/pectin-core/test/pectin-core.test.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import { rollup, RollupOptions, RollupOutput, OutputOptions, OutputChunk } from 'rollup'; 3 | import Tacks = require('tacks'); 4 | import tempy = require('tempy'); 5 | import pMap = require('p-map'); 6 | import pectinCore from '../lib/pectin-core'; 7 | 8 | const { Dir, File, Symlink } = Tacks; 9 | 10 | // avoid polluting other test state 11 | const REPO_ROOT = path.resolve('.'); 12 | 13 | afterEach(() => { 14 | process.chdir(REPO_ROOT); 15 | }); 16 | 17 | function createFixture(pkgSpec): string { 18 | const cwd = tempy.directory(); 19 | const fixture = new Tacks( 20 | Dir({ 21 | // .babelrc is necessary to avoid an 22 | // implicit resolution from repo root 23 | '.babelrc': File({ 24 | presets: ['@babel/env', '@babel/preset-react'], 25 | plugins: ['@babel/plugin-syntax-dynamic-import'], 26 | }), 27 | // spicy symlink necessary due to explicit cwd config 28 | 'node_modules': Symlink(path.relative(cwd, path.join(REPO_ROOT, 'node_modules'))), 29 | ...pkgSpec, 30 | }) 31 | ); 32 | 33 | fixture.create(cwd); 34 | 35 | return cwd; 36 | } 37 | 38 | async function generateResults(configs: RollupOptions[]): Promise { 39 | const results = await pMap(configs, ({ output: outputOptions, ...inputOptions }) => 40 | rollup(inputOptions).then(bundle => 41 | Promise.all( 42 | (outputOptions as OutputOptions[]).map((opts: OutputOptions) => 43 | bundle.generate(opts) 44 | ) 45 | ).then((generated: RollupOutput[]) => 46 | generated.reduce( 47 | (arr: OutputChunk[], result) => 48 | arr.concat((result.output as unknown) as OutputChunk), 49 | [] 50 | ) 51 | ) 52 | ) 53 | ); 54 | 55 | // flatten results 56 | return results.reduce((arr, result) => arr.concat(result), []); 57 | } 58 | 59 | describe('pectin-core', () => { 60 | it('inlines SVG via pkg.rollup.inlineSVG', async () => { 61 | const pkg = { 62 | name: 'inline-svg-data-uri', 63 | main: 'dist/index.js', 64 | rollup: { 65 | inlineSVG: true, 66 | }, 67 | }; 68 | const cwd = createFixture({ 69 | 'package.json': File(pkg), 70 | 'src': Dir({ 71 | 'test.svg': File( 72 | `` 73 | ), 74 | 'index.js': File(` 75 | import svgTest from './test.svg'; 76 | 77 | export default svgTest; 78 | `), 79 | }), 80 | }); 81 | 82 | const configs = await pectinCore(pkg, { cwd }); 83 | const results = await generateResults(configs); 84 | const [entry] = results; 85 | 86 | expect(entry.code).toMatchInlineSnapshot(` 87 | "'use strict'; 88 | 89 | var svgTest = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiAvPg=='; 90 | 91 | module.exports = svgTest; 92 | " 93 | `); 94 | }); 95 | 96 | it('customizes input with pkg.rollup.rootDir', async () => { 97 | const pkg = { 98 | name: 'rollup-root-dir', 99 | main: 'dist/rollup-root-dir.js', 100 | rollup: { 101 | rootDir: 'modules', 102 | }, 103 | }; 104 | const cwd = createFixture({ 105 | 'package.json': File(pkg), 106 | 'modules': Dir({ 107 | 'rollup-root-dir.js': File(`export default 'success';`), 108 | }), 109 | }); 110 | 111 | const configs = await pectinCore(pkg, { cwd }); 112 | const results = await generateResults(configs); 113 | const [entry] = results; 114 | 115 | expect(entry.fileName).toBe('rollup-root-dir.js'); 116 | expect(entry.code).toMatchInlineSnapshot(` 117 | "'use strict'; 118 | 119 | var rollupRootDir = 'success'; 120 | 121 | module.exports = rollupRootDir; 122 | " 123 | `); 124 | }); 125 | 126 | it('overrides input with pkg.rollup.input', async () => { 127 | const pkg = { 128 | name: 'rollup-input', 129 | main: 'dist/rollup-input.js', 130 | rollup: { 131 | input: 'app.js', 132 | }, 133 | }; 134 | const cwd = createFixture({ 135 | 'package.json': File(pkg), 136 | 'app.js': File(`export default 'app';`), 137 | }); 138 | 139 | const configs = await pectinCore(pkg, { cwd }); 140 | const results = await generateResults(configs); 141 | const [entry] = results; 142 | 143 | expect(entry.code).toMatchInlineSnapshot(` 144 | "'use strict'; 145 | 146 | var app = 'app'; 147 | 148 | module.exports = app; 149 | " 150 | `); 151 | }); 152 | 153 | it('resolves pkgPath from cwd', async () => { 154 | const pkg = { 155 | name: 'from-cwd', 156 | main: 'dist/index.js', 157 | }; 158 | const cwd = createFixture({ 159 | 'package.json': File(pkg), 160 | 'src': Dir({ 161 | 'index.js': File(`export default 'cwd';`), 162 | }), 163 | }); 164 | 165 | process.chdir(cwd); 166 | 167 | const configs = await pectinCore(pkg /* , { cwd } */); 168 | const results = await generateResults(configs); 169 | const [entry] = results; 170 | 171 | expect(entry.code).toMatchInlineSnapshot(` 172 | "'use strict'; 173 | 174 | var index = 'cwd'; 175 | 176 | module.exports = index; 177 | " 178 | `); 179 | }); 180 | 181 | it('throws an error when no pkg.main supplied', async () => { 182 | const pkg = { 183 | name: 'no-pkg-main', 184 | }; 185 | const cwd = createFixture({ 186 | 'package.json': File(pkg), 187 | }); 188 | 189 | // required to normalize snapshot 190 | process.chdir(cwd); 191 | 192 | try { 193 | await pectinCore(pkg, { cwd }); 194 | } catch (err) { 195 | expect(err).toMatchInlineSnapshot( 196 | `[TypeError: required field 'main' missing in package.json]` 197 | ); 198 | } 199 | 200 | expect.assertions(1); 201 | }); 202 | 203 | it('generates chunked module output', async () => { 204 | const pkg = { 205 | name: 'chunked-module-outputs', 206 | main: './dist/index.js', 207 | module: './dist/index.esm.js', 208 | rollup: { 209 | // can't use [hash] in chunks because it changes _every_ execution 210 | chunkFileNames: '[name].[format].js', 211 | }, 212 | }; 213 | const cwd = createFixture({ 214 | 'package.json': File(pkg), 215 | 'src': Dir({ 216 | 'chunky-bacon.js': File(`export default '_why';`), 217 | 'index.js': File(` 218 | export default function main() { 219 | return import('./chunky-bacon'); 220 | }; 221 | `), 222 | }), 223 | }); 224 | 225 | const configs = await pectinCore(pkg, { cwd }); 226 | const results = await generateResults(configs); 227 | 228 | const fileNames = results.map(result => `dist/${result.fileName}`); 229 | const [cjsEntry, cjsChunk, esmEntry, esmChunk] = results.map( 230 | result => `// dist/${result.fileName}\n${result.code}` 231 | ); 232 | 233 | expect(fileNames).toStrictEqual([ 234 | 'dist/index.js', 235 | 'dist/chunky-bacon.cjs.js', 236 | 'dist/index.esm.js', 237 | 'dist/chunky-bacon.esm.js', 238 | ]); 239 | 240 | expect(cjsEntry).toMatchInlineSnapshot(` 241 | "// dist/index.js 242 | 'use strict'; 243 | 244 | function main() { 245 | return new Promise(function (resolve) { resolve(require('./chunky-bacon.cjs.js')); }); 246 | } 247 | 248 | module.exports = main; 249 | " 250 | `); 251 | expect(cjsChunk).toMatchInlineSnapshot(` 252 | "// dist/chunky-bacon.cjs.js 253 | 'use strict'; 254 | 255 | var chunkyBacon = '_why'; 256 | 257 | exports.default = chunkyBacon; 258 | " 259 | `); 260 | expect(esmEntry).toMatchInlineSnapshot(` 261 | "// dist/index.esm.js 262 | function main() { 263 | return import('./chunky-bacon.esm.js'); 264 | } 265 | 266 | export default main; 267 | " 268 | `); 269 | expect(esmChunk).toMatchInlineSnapshot(` 270 | "// dist/chunky-bacon.esm.js 271 | var chunkyBacon = '_why'; 272 | 273 | export default chunkyBacon; 274 | " 275 | `); 276 | }); 277 | 278 | it('generates basic pkg.browser output', async () => { 279 | const pkg = { 280 | name: 'basic-browser-outputs', 281 | main: './dist/index.js', 282 | module: './dist/index.esm.js', 283 | browser: './dist/index.browser.js', 284 | }; 285 | const cwd = createFixture({ 286 | 'package.json': File(pkg), 287 | 'src': Dir({ 288 | 'index.js': File(` 289 | export default class Basic { 290 | constructor() { 291 | this.isBrowser = process.env.BROWSER; 292 | } 293 | }; 294 | `), 295 | }), 296 | }); 297 | 298 | const configs = await pectinCore(pkg, { cwd }); 299 | const results = await generateResults(configs); 300 | 301 | const fileNames = results.map(result => `dist/${result.fileName}`); 302 | const [cjsMain, esmModule, cjsBrowser] = results.map( 303 | result => `// dist/${result.fileName}\n${result.code}` 304 | ); 305 | 306 | expect(fileNames).toStrictEqual([ 307 | 'dist/index.js', 308 | 'dist/index.esm.js', 309 | 'dist/index.browser.js', 310 | ]); 311 | expect(cjsMain).toMatchInlineSnapshot(` 312 | "// dist/index.js 313 | 'use strict'; 314 | 315 | function _classCallCheck(instance, Constructor) { 316 | if (!(instance instanceof Constructor)) { 317 | throw new TypeError(\\"Cannot call a class as a function\\"); 318 | } 319 | } 320 | 321 | var Basic = function Basic() { 322 | _classCallCheck(this, Basic); 323 | 324 | this.isBrowser = false; 325 | }; 326 | 327 | module.exports = Basic; 328 | " 329 | `); 330 | expect(esmModule).toMatchInlineSnapshot(` 331 | "// dist/index.esm.js 332 | function _classCallCheck(instance, Constructor) { 333 | if (!(instance instanceof Constructor)) { 334 | throw new TypeError(\\"Cannot call a class as a function\\"); 335 | } 336 | } 337 | 338 | var Basic = function Basic() { 339 | _classCallCheck(this, Basic); 340 | 341 | this.isBrowser = false; 342 | }; 343 | 344 | export default Basic; 345 | " 346 | `); 347 | expect(cjsBrowser).toMatchInlineSnapshot(` 348 | "// dist/index.browser.js 349 | 'use strict'; 350 | 351 | function _classCallCheck(instance, Constructor) { 352 | if (!(instance instanceof Constructor)) { 353 | throw new TypeError(\\"Cannot call a class as a function\\"); 354 | } 355 | } 356 | 357 | var Basic = function Basic() { 358 | _classCallCheck(this, Basic); 359 | 360 | this.isBrowser = true; 361 | }; 362 | 363 | module.exports = Basic; 364 | " 365 | `); 366 | }); 367 | 368 | it('generates advanced pkg.browser outputs', async () => { 369 | const pkg = { 370 | name: 'advanced-browser-outputs', 371 | main: './dist/index.js', 372 | module: './dist/index.esm.js', 373 | browser: { 374 | './dist/index.js': './dist/index.browser.js', 375 | './dist/index.esm.js': './dist/index.module.browser.js', 376 | }, 377 | dependencies: { 378 | '@babel/runtime': '^7.0.0', 379 | }, 380 | }; 381 | const cwd = createFixture({ 382 | 'package.json': File(pkg), 383 | 'src': Dir({ 384 | 'index.js': File(` 385 | export default class Advanced { 386 | constructor() { 387 | this.isBrowser = process.env.BROWSER; 388 | } 389 | }; 390 | `), 391 | }), 392 | }); 393 | 394 | const configs = await pectinCore(pkg, { cwd }); 395 | const results = await generateResults(configs); 396 | 397 | const fileNames = results.map(result => `dist/${result.fileName}`); 398 | const [cjsMain, esmModule, cjsBrowser, esmBrowser] = results.map( 399 | result => `// dist/${result.fileName}\n${result.code}` 400 | ); 401 | 402 | expect(fileNames).toStrictEqual([ 403 | 'dist/index.js', 404 | 'dist/index.esm.js', 405 | 'dist/index.browser.js', 406 | 'dist/index.module.browser.js', 407 | ]); 408 | 409 | expect(cjsMain).toMatchInlineSnapshot(` 410 | "// dist/index.js 411 | 'use strict'; 412 | 413 | function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } 414 | 415 | var _classCallCheck = _interopDefault(require('@babel/runtime/helpers/classCallCheck')); 416 | 417 | var Advanced = function Advanced() { 418 | _classCallCheck(this, Advanced); 419 | 420 | this.isBrowser = false; 421 | }; 422 | 423 | module.exports = Advanced; 424 | " 425 | `); 426 | expect(esmModule).toMatchInlineSnapshot(` 427 | "// dist/index.esm.js 428 | import _classCallCheck from '@babel/runtime/helpers/esm/classCallCheck'; 429 | 430 | var Advanced = function Advanced() { 431 | _classCallCheck(this, Advanced); 432 | 433 | this.isBrowser = false; 434 | }; 435 | 436 | export default Advanced; 437 | " 438 | `); 439 | expect(cjsBrowser).toMatchInlineSnapshot(` 440 | "// dist/index.browser.js 441 | 'use strict'; 442 | 443 | function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } 444 | 445 | var _classCallCheck = _interopDefault(require('@babel/runtime/helpers/classCallCheck')); 446 | 447 | var Advanced = function Advanced() { 448 | _classCallCheck(this, Advanced); 449 | 450 | this.isBrowser = true; 451 | }; 452 | 453 | module.exports = Advanced; 454 | " 455 | `); 456 | expect(esmBrowser).toMatchInlineSnapshot(` 457 | "// dist/index.module.browser.js 458 | import _classCallCheck from '@babel/runtime/helpers/esm/classCallCheck'; 459 | 460 | var Advanced = function Advanced() { 461 | _classCallCheck(this, Advanced); 462 | 463 | this.isBrowser = true; 464 | }; 465 | 466 | export default Advanced; 467 | " 468 | `); 469 | }); 470 | 471 | it('generates pkg.unpkg UMD output for unscoped package with peers', async () => { 472 | const pkg = { 473 | name: 'unpkg-umd-output', 474 | main: './dist/index.js', 475 | module: './dist/index.esm.js', 476 | unpkg: './dist/index.min.js', 477 | peerDependencies: { 478 | react: '*', 479 | }, 480 | dependencies: { 481 | '@babel/runtime': '^7.0.0', 482 | }, 483 | }; 484 | const cwd = createFixture({ 485 | 'package.json': File(pkg), 486 | 'src': Dir({ 487 | 'index.js': File( 488 | 'import React from "react"; export default () => React.render("woo");' 489 | ), 490 | }), 491 | }); 492 | 493 | const configs = await pectinCore(pkg, { cwd }); 494 | const results = await generateResults(configs); 495 | 496 | const fileNames = results.map(result => `dist/${result.fileName}`); 497 | const minOutput = results.pop().code; 498 | const umdOutput = results.pop().code; 499 | 500 | expect(fileNames).toContain('dist/index.dev.js'); 501 | expect(fileNames).toContain('dist/index.min.js'); 502 | expect(umdOutput).toMatchInlineSnapshot(` 503 | "(function (global, factory) { 504 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('react')) : 505 | typeof define === 'function' && define.amd ? define(['react'], factory) : 506 | (global = global || self, global.UnpkgUmdOutput = factory(global.React)); 507 | }(this, (function (React) { 'use strict'; 508 | 509 | React = React && React.hasOwnProperty('default') ? React['default'] : React; 510 | 511 | var index = (function () { 512 | return React.render(\\"woo\\"); 513 | }); 514 | 515 | return index; 516 | 517 | }))); 518 | " 519 | `); 520 | expect(minOutput).toMatchInlineSnapshot(` 521 | "!function(e,t){\\"object\\"==typeof exports&&\\"undefined\\"!=typeof module?module.exports=t(require(\\"react\\")):\\"function\\"==typeof define&&define.amd?define([\\"react\\"],t):(e=e||self).UnpkgUmdOutput=t(e.React)}(this,(function(e){\\"use strict\\";e=e&&e.hasOwnProperty(\\"default\\")?e.default:e;return function(){return e.render(\\"woo\\")}})); 522 | " 523 | `); 524 | }); 525 | 526 | it('generates pkg.unpkg UMD output for scoped package without peers', async () => { 527 | const pkg = { 528 | name: '@unpkg/scoped-umd', 529 | main: './dist/index.js', 530 | unpkg: './dist/index.min.js', 531 | }; 532 | const cwd = createFixture({ 533 | 'package.json': File(pkg), 534 | 'src': Dir({ 535 | 'index.js': File(` 536 | export default function main() { 537 | console.log("yay"); 538 | 539 | if (process.env.NODE_ENV === 'production') { 540 | console.log('hooray'); 541 | } 542 | } 543 | `), 544 | }), 545 | }); 546 | 547 | const configs = await pectinCore(pkg, { cwd }); 548 | const results = await generateResults(configs); 549 | 550 | const minOutput = results.pop().code; 551 | const umdOutput = results.pop().code; 552 | 553 | expect(umdOutput).toMatchInlineSnapshot(` 554 | "(function (global, factory) { 555 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 556 | typeof define === 'function' && define.amd ? define(factory) : 557 | (global = global || self, global.ScopedUmd = factory()); 558 | }(this, (function () { 'use strict'; 559 | 560 | function main() { 561 | console.log(\\"yay\\"); 562 | } 563 | 564 | return main; 565 | 566 | }))); 567 | " 568 | `); 569 | expect(minOutput).toMatchInlineSnapshot(` 570 | "!function(e,o){\\"object\\"==typeof exports&&\\"undefined\\"!=typeof module?module.exports=o():\\"function\\"==typeof define&&define.amd?define(o):(e=e||self).ScopedUmd=o()}(this,(function(){\\"use strict\\";return function(){console.log(\\"yay\\"),console.log(\\"hooray\\")}})); 571 | " 572 | `); 573 | }); 574 | 575 | it('interpolates process.env.VERSION with pkg.version', async () => { 576 | const pkg = { 577 | name: 'interpolates-version', 578 | main: 'dist/index.js', 579 | version: '1.2.3-alpha.0+deadbeef', 580 | }; 581 | const cwd = createFixture({ 582 | 'package.json': File(pkg), 583 | 'src': Dir({ 584 | 'index.js': File(` 585 | export default function main() { 586 | console.log(process.env.VERSION); 587 | } 588 | `), 589 | }), 590 | }); 591 | 592 | const configs = await pectinCore(pkg, { cwd }); 593 | const results = await generateResults(configs); 594 | const [cjs] = results; 595 | 596 | expect(cjs.code).toMatch('console.log("1.2.3-alpha.0+deadbeef");'); 597 | }); 598 | 599 | it('works all together', async () => { 600 | const pkg = { 601 | name: 'integration', 602 | main: 'dist/index.js', 603 | module: 'dist/index.esm.js', 604 | rollup: { 605 | inlineSVG: true, 606 | }, 607 | dependencies: { 608 | '@babel/runtime': '^7.0.0', 609 | 'react': '*', 610 | }, 611 | }; 612 | const cwd = createFixture({ 613 | 'package.json': File(pkg), 614 | 'src': Dir({ 615 | 'test.svg': File( 616 | `` 617 | ), 618 | // a class is a lot more interesting output 619 | 'index.js': File(` 620 | import React from 'react'; 621 | import svgTest from './test.svg'; 622 | 623 | export default class Foo extends React.Component { 624 | render() { 625 | return
{svgTest}
; 626 | } 627 | }; 628 | `), 629 | }), 630 | }); 631 | 632 | const configs = await pectinCore(pkg, { cwd }); 633 | const results = await generateResults(configs); 634 | const [cjs, esm] = results; 635 | 636 | expect(esm.code).toMatchInlineSnapshot(` 637 | "import _classCallCheck from '@babel/runtime/helpers/esm/classCallCheck'; 638 | import _createClass from '@babel/runtime/helpers/esm/createClass'; 639 | import _possibleConstructorReturn from '@babel/runtime/helpers/esm/possibleConstructorReturn'; 640 | import _getPrototypeOf from '@babel/runtime/helpers/esm/getPrototypeOf'; 641 | import _inherits from '@babel/runtime/helpers/esm/inherits'; 642 | import React from 'react'; 643 | 644 | var svgTest = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pjxzdmcgdmlld0JveD0iMCAwIDE1MS41NyAxNTEuNTciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGxpbmUgeDE9IjQ3LjU3IiB4Mj0iMTAzLjk5IiB5MT0iMTAzLjk5IiB5Mj0iNDcuNTciLz48bGluZSB4MT0iNDUuOCIgeDI9IjEwNS43IiB5MT0iNDUuODciIHkyPSIxMDUuNzciLz48L3N2Zz4='; 645 | 646 | var Foo = 647 | /*#__PURE__*/ 648 | function (_React$Component) { 649 | _inherits(Foo, _React$Component); 650 | 651 | function Foo() { 652 | _classCallCheck(this, Foo); 653 | 654 | return _possibleConstructorReturn(this, _getPrototypeOf(Foo).apply(this, arguments)); 655 | } 656 | 657 | _createClass(Foo, [{ 658 | key: \\"render\\", 659 | value: function render() { 660 | return React.createElement(\\"div\\", null, svgTest); 661 | } 662 | }]); 663 | 664 | return Foo; 665 | }(React.Component); 666 | 667 | export default Foo; 668 | " 669 | `); 670 | expect(cjs.code).toMatch("'use strict';"); 671 | expect(cjs.code).toMatch('_interopDefault'); 672 | expect(cjs.code).toMatch("require('@babel/runtime/helpers/createClass')"); 673 | expect(cjs.code).toMatch('module.exports = Foo;'); 674 | // transpiled code is otherwise identical 675 | }); 676 | }); 677 | -------------------------------------------------------------------------------- /packages/pectin-babelrc/test/pectin-babelrc.test.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import Tacks = require('tacks'); 3 | import tempy = require('tempy'); 4 | import pectinBabelrc from '../lib/pectin-babelrc'; 5 | 6 | const { Dir, File, Symlink } = Tacks; 7 | 8 | // spicy symlink prep 9 | const REPO_ROOT = path.resolve('.'); 10 | const BABEL_RUNTIME_DEFAULT_VERSION = require('@babel/runtime/package.json').version; 11 | const BABEL_RUNTIME_COREJS2_VERSION = require('@babel/runtime-corejs2/package.json').version; 12 | const BABEL_RUNTIME_COREJS3_VERSION = require('@babel/runtime-corejs3/package.json').version; 13 | 14 | function createFixture(spec): string { 15 | const cwd = tempy.directory(); 16 | 17 | new Tacks( 18 | Dir({ 19 | // spicy symlink necessary due to potential runtime package resolution 20 | // eslint-disable-next-line @typescript-eslint/camelcase 21 | node_modules: Symlink(path.relative(cwd, path.join(REPO_ROOT, 'node_modules'))), 22 | ...spec, 23 | }) 24 | ).create(cwd); 25 | 26 | return cwd; 27 | } 28 | 29 | // tempy creates subdirectories with hexadecimal names that are 32 characters long 30 | const TEMP_DIR_REGEXP = /([^\s"]*[\\/][0-9a-f]{32})([^\s"]*)/g; 31 | // the excluded quotes are due to other snapshot serializers mutating the raw input 32 | 33 | expect.addSnapshotSerializer({ 34 | test(val) { 35 | return typeof val === 'string' && TEMP_DIR_REGEXP.test(val); 36 | }, 37 | // eslint-disable-next-line @typescript-eslint/ban-ts-ignore 38 | // @ts-ignore (the types are wrong, yet again) 39 | serialize(val: string, config, indentation: string, depth: number) { 40 | const str = val.replace(TEMP_DIR_REGEXP, (match, cwd, subPath) => 41 | path.join('', subPath) 42 | ); 43 | 44 | // top-level strings don't need quotes, but nested ones do (object properties, etc) 45 | return depth ? `"${str}"` : str; 46 | }, 47 | }); 48 | 49 | describe('pectin-babelrc', () => { 50 | describe('with implicit cwd', () => { 51 | afterEach(() => { 52 | // avoid polluting other test state 53 | process.chdir(REPO_ROOT); 54 | }); 55 | 56 | it('begins search for config from process.cwd()', async () => { 57 | const pkg = { 58 | name: 'implicit-cwd', 59 | }; 60 | const cwd = createFixture({ 61 | '.babelrc': File({ 62 | presets: ['@babel/env'], 63 | }), 64 | 'package.json': File(pkg), 65 | }); 66 | 67 | process.chdir(cwd); 68 | 69 | const rc = await pectinBabelrc(pkg); 70 | 71 | expect(rc).toHaveProperty('cwd', cwd); 72 | }); 73 | }); 74 | 75 | it('generates config for rollup-plugin-babel', async () => { 76 | const pkg = { 77 | name: 'babel-7-config', 78 | dependencies: { 79 | lodash: '^4.17.4', 80 | }, 81 | }; 82 | const cwd = createFixture({ 83 | '.babelrc': File({ 84 | presets: ['@babel/env'], 85 | }), 86 | 'package.json': File(pkg), 87 | }); 88 | const rc = await pectinBabelrc(pkg, cwd); 89 | 90 | expect(rc).toMatchInlineSnapshot(` 91 | Object { 92 | "babelrc": false, 93 | "cwd": "", 94 | "exclude": "node_modules/**", 95 | "extensions": Array [ 96 | ".js", 97 | ".jsx", 98 | ".es6", 99 | ".es", 100 | ".mjs", 101 | ".ts", 102 | ".tsx", 103 | ], 104 | "plugins": Array [], 105 | "presets": Array [ 106 | "@babel/env", 107 | ], 108 | } 109 | `); 110 | }); 111 | 112 | it('enables runtimeHelpers when @babel/runtime is a dependency', async () => { 113 | const pkg = { 114 | name: 'helpers-runtime', 115 | dependencies: { 116 | '@babel/runtime': '^7.0.0', 117 | 'lodash': '^4.17.4', 118 | }, 119 | }; 120 | const cwd = createFixture({ 121 | '.babelrc': File({ 122 | presets: ['@babel/preset-env'], 123 | }), 124 | 'package.json': File(pkg), 125 | }); 126 | const rc = await pectinBabelrc(pkg, cwd); 127 | 128 | expect(rc).toHaveProperty('runtimeHelpers', true); 129 | }); 130 | 131 | it('does not mutate cached config', async () => { 132 | const pkg = { 133 | name: 'no-mutate', 134 | dependencies: { 135 | lodash: '^4.17.4', 136 | }, 137 | }; 138 | const cwd = createFixture({ 139 | '.babelrc': File({ 140 | presets: ['@babel/preset-env'], 141 | plugins: ['lodash'], 142 | }), 143 | 'package.json': File(pkg), 144 | }); 145 | 146 | const opts = await pectinBabelrc(pkg, cwd); 147 | 148 | // cosmiconfig caches live objects 149 | opts.foo = 'bar'; 150 | 151 | const rc = await pectinBabelrc(pkg, cwd); 152 | 153 | expect(rc).not.toHaveProperty('foo'); 154 | }); 155 | 156 | it('finds babel.config.js config', async () => { 157 | const pkg = { 158 | name: 'with-babel-config-js', 159 | }; 160 | const cwd = createFixture({ 161 | 'babel.config.js': File(` 162 | module.exports = { 163 | presets: ['@babel/preset-env'], 164 | }; 165 | `), 166 | 'package.json': File(pkg), 167 | }); 168 | 169 | const rc = await pectinBabelrc(pkg, cwd); 170 | 171 | expect(rc).toMatchInlineSnapshot(` 172 | Object { 173 | "babelrc": false, 174 | "cwd": "", 175 | "exclude": "node_modules/**", 176 | "extensions": Array [ 177 | ".js", 178 | ".jsx", 179 | ".es6", 180 | ".es", 181 | ".mjs", 182 | ".ts", 183 | ".tsx", 184 | ], 185 | "plugins": Array [], 186 | "presets": Array [ 187 | "@babel/preset-env", 188 | ], 189 | } 190 | `); 191 | }); 192 | 193 | it('finds .babelrc.js config', async () => { 194 | const pkg = { 195 | name: 'with-babelrc-js', 196 | }; 197 | const cwd = createFixture({ 198 | '.babelrc.js': File(` 199 | module.exports = { 200 | presets: ['@babel/preset-env'], 201 | }; 202 | `), 203 | 'package.json': File(pkg), 204 | }); 205 | const rc = await pectinBabelrc(pkg, cwd); 206 | 207 | expect(rc).toMatchInlineSnapshot(` 208 | Object { 209 | "babelrc": false, 210 | "cwd": "", 211 | "exclude": "node_modules/**", 212 | "extensions": Array [ 213 | ".js", 214 | ".jsx", 215 | ".es6", 216 | ".es", 217 | ".mjs", 218 | ".ts", 219 | ".tsx", 220 | ], 221 | "plugins": Array [], 222 | "presets": Array [ 223 | "@babel/preset-env", 224 | ], 225 | } 226 | `); 227 | }); 228 | 229 | it('finds pkg.babel config', async () => { 230 | const pkg = { 231 | name: 'with-babel-prop', 232 | babel: { 233 | presets: ['@babel/preset-env'], 234 | }, 235 | }; 236 | const cwd = createFixture({ 237 | 'package.json': File(pkg), 238 | }); 239 | const rc = await pectinBabelrc(pkg, cwd); 240 | 241 | expect(rc).toMatchInlineSnapshot(` 242 | Object { 243 | "babelrc": false, 244 | "cwd": "", 245 | "exclude": "node_modules/**", 246 | "extensions": Array [ 247 | ".js", 248 | ".jsx", 249 | ".es6", 250 | ".es", 251 | ".mjs", 252 | ".ts", 253 | ".tsx", 254 | ], 255 | "plugins": Array [], 256 | "presets": Array [ 257 | "@babel/preset-env", 258 | ], 259 | } 260 | `); 261 | }); 262 | 263 | it('does not duplicate simple runtime transform', async () => { 264 | const pkg = { 265 | name: 'no-duplicate-transform-simple', 266 | dependencies: { 267 | '@babel/runtime': '^7.0.0', 268 | }, 269 | }; 270 | const cwd = createFixture({ 271 | '.babelrc': File({ 272 | presets: ['@babel/preset-env'], 273 | plugins: ['@babel/plugin-transform-runtime', 'lodash'], 274 | }), 275 | 'package.json': File(pkg), 276 | }); 277 | const opts = await pectinBabelrc(pkg, cwd, { format: 'esm' }); 278 | 279 | expect(opts).toHaveProperty('plugins', [ 280 | '@babel/plugin-syntax-dynamic-import', 281 | [ 282 | '@babel/plugin-transform-runtime', 283 | { useESModules: true, version: BABEL_RUNTIME_DEFAULT_VERSION }, 284 | ], 285 | 'lodash', 286 | ]); 287 | }); 288 | 289 | it('does not duplicate advanced runtime transform', async () => { 290 | const pkg = { 291 | name: 'no-duplicate-transform-advanced', 292 | dependencies: { 293 | '@babel/runtime': '^7.0.0', 294 | }, 295 | }; 296 | const cwd = createFixture({ 297 | '.babelrc': File({ 298 | presets: ['@babel/env'], 299 | plugins: [ 300 | 'graphql-tag', 301 | [ 302 | '@babel/transform-runtime', 303 | { 304 | corejs: true, 305 | }, 306 | ], 307 | ], 308 | }), 309 | 'package.json': File(pkg), 310 | }); 311 | const opts = await pectinBabelrc(pkg, cwd, { format: 'esm' }); 312 | 313 | expect(opts).toHaveProperty('plugins', [ 314 | '@babel/plugin-syntax-dynamic-import', 315 | 'graphql-tag', 316 | [ 317 | '@babel/transform-runtime', 318 | { 319 | corejs: true, 320 | useESModules: true, 321 | version: BABEL_RUNTIME_DEFAULT_VERSION, 322 | }, 323 | ], 324 | ]); 325 | }); 326 | 327 | it('adds missing config to advanced runtime transform', async () => { 328 | const pkg = { 329 | name: 'add-config-transform-advanced', 330 | dependencies: { 331 | '@babel/runtime': '^7.0.0', 332 | }, 333 | }; 334 | const cwd = createFixture({ 335 | '.babelrc': File({ 336 | presets: ['@babel/env'], 337 | // admittedly weird... 338 | plugins: [['@babel/plugin-transform-runtime']], 339 | }), 340 | 'package.json': File(pkg), 341 | }); 342 | const opts = await pectinBabelrc(pkg, cwd, { format: 'esm' }); 343 | 344 | expect(opts).toHaveProperty('plugins', [ 345 | '@babel/plugin-syntax-dynamic-import', 346 | [ 347 | '@babel/plugin-transform-runtime', 348 | { useESModules: true, version: BABEL_RUNTIME_DEFAULT_VERSION }, 349 | ], 350 | ]); 351 | }); 352 | 353 | it('passes { corejs: 2 } to runtime transform when alternate dependency detected', async () => { 354 | const pkg = { 355 | name: 'add-config-transform-advanced', 356 | dependencies: { 357 | '@babel/runtime-corejs2': '^7.0.0', 358 | }, 359 | }; 360 | const cwd = createFixture({ 361 | '.babelrc': File({ 362 | presets: ['@babel/env'], 363 | }), 364 | 'package.json': File(pkg), 365 | }); 366 | const opts = await pectinBabelrc(pkg, cwd, { format: 'esm' }); 367 | 368 | expect(opts).toHaveProperty('plugins', [ 369 | '@babel/plugin-syntax-dynamic-import', 370 | [ 371 | '@babel/plugin-transform-runtime', 372 | { useESModules: true, corejs: 2, version: BABEL_RUNTIME_COREJS2_VERSION }, 373 | ], 374 | ]); 375 | }); 376 | 377 | it('passes { corejs: 3 } to runtime transform when alternate dependency detected', async () => { 378 | const pkg = { 379 | name: 'add-config-transform-advanced', 380 | dependencies: { 381 | '@babel/runtime-corejs3': '^7.4.0', 382 | }, 383 | }; 384 | const cwd = createFixture({ 385 | '.babelrc': File({ 386 | presets: ['@babel/env'], 387 | }), 388 | 'package.json': File(pkg), 389 | }); 390 | const opts = await pectinBabelrc(pkg, cwd, { format: 'esm' }); 391 | 392 | expect(opts).toHaveProperty('plugins', [ 393 | '@babel/plugin-syntax-dynamic-import', 394 | [ 395 | '@babel/plugin-transform-runtime', 396 | { useESModules: true, corejs: 3, version: BABEL_RUNTIME_COREJS3_VERSION }, 397 | ], 398 | ]); 399 | }); 400 | 401 | it('does not duplicate existing @babel/syntax-dynamic-import plugin', async () => { 402 | const pkg = { 403 | name: 'no-duplicate-syntax', 404 | dependencies: { 405 | lodash: '*', 406 | }, 407 | }; 408 | const cwd = createFixture({ 409 | '.babelrc': File({ 410 | presets: ['@babel/preset-env'], 411 | plugins: ['lodash', '@babel/syntax-dynamic-import'], 412 | }), 413 | 'package.json': File(pkg), 414 | }); 415 | const opts = await pectinBabelrc(pkg, cwd, { format: 'esm' }); 416 | 417 | expect(opts).toHaveProperty('plugins', ['lodash', '@babel/syntax-dynamic-import']); 418 | }); 419 | 420 | it('does not add syntax-dynamic-import plugin to non-ESM format', async () => { 421 | const pkg = { 422 | name: 'no-cjs-dynamic-import', 423 | dependencies: { 424 | lodash: '*', 425 | }, 426 | }; 427 | const cwd = createFixture({ 428 | '.babelrc': File({ 429 | presets: ['@babel/preset-env'], 430 | plugins: ['lodash'], 431 | }), 432 | 'package.json': File(pkg), 433 | }); 434 | const opts = await pectinBabelrc(pkg, cwd, { format: 'cjs' }); 435 | 436 | expect(opts).toHaveProperty('plugins', ['lodash']); 437 | }); 438 | 439 | it('throws an error when .babelrc preset is missing', async () => { 440 | const pkg = { 441 | name: 'no-presets', 442 | dependencies: { 443 | lodash: '^4.17.4', 444 | }, 445 | }; 446 | const cwd = createFixture({ 447 | '.babelrc': File({ 448 | plugins: ['lodash'], 449 | }), 450 | 'package.json': File(pkg), 451 | }); 452 | 453 | try { 454 | await pectinBabelrc(pkg, cwd); 455 | } catch (err) { 456 | expect(err.message).toMatchInlineSnapshot( 457 | `"At least one preset (like @babel/preset-env) is required in .babelrc"` 458 | ); 459 | } 460 | 461 | expect.assertions(1); 462 | }); 463 | 464 | it('throws an error when pkg.babel preset is missing', async () => { 465 | const pkg = { 466 | name: 'no-prop-presets', 467 | babel: { 468 | plugins: ['lodash'], 469 | }, 470 | dependencies: { 471 | lodash: '^4.17.4', 472 | }, 473 | }; 474 | const cwd = createFixture({ 475 | 'package.json': File(pkg), 476 | }); 477 | 478 | try { 479 | await pectinBabelrc(pkg, cwd); 480 | } catch (err) { 481 | expect(err.message).toMatchInlineSnapshot( 482 | `"At least one preset (like @babel/preset-env) is required in \\"babel\\" config block of package.json"` 483 | ); 484 | } 485 | 486 | expect.assertions(1); 487 | }); 488 | 489 | it('throws an error when no babel config found', async () => { 490 | const pkg = { 491 | name: 'no-babel-config', 492 | dependencies: { 493 | lodash: '^4.17.4', 494 | }, 495 | }; 496 | const cwd = createFixture({ 497 | 'package.json': File(pkg), 498 | }); 499 | 500 | try { 501 | await pectinBabelrc(pkg, cwd); 502 | } catch (err) { 503 | expect(err.message).toMatchInlineSnapshot( 504 | `"Babel configuration is required for no-babel-config, but no config file was found."` 505 | ); 506 | } 507 | 508 | expect.assertions(1); 509 | }); 510 | 511 | it('works all together', async () => { 512 | const pkg1 = { 513 | name: 'pkg1', 514 | }; 515 | const pkg2 = { 516 | name: 'pkg2', 517 | babel: { 518 | presets: ['@babel/env'], 519 | plugins: ['lodash'], 520 | }, 521 | }; 522 | const pkg3 = { 523 | name: 'pkg3', 524 | dependencies: { 525 | '@babel/runtime': '*', 526 | }, 527 | }; 528 | const pkg4 = { 529 | name: 'pkg4', 530 | dependencies: { 531 | '@babel/runtime': '*', 532 | }, 533 | }; 534 | 535 | const cwd = createFixture({ 536 | 'package.json': File({ 537 | name: 'monorepo', 538 | private: true, 539 | babel: { 540 | presets: ['@babel/preset-env'], 541 | plugins: ['@babel/plugin-proposal-object-rest-spread'], 542 | }, 543 | }), 544 | 'packages': Dir({ 545 | pkg1: Dir({ 546 | 'package.json': File(pkg1), 547 | }), 548 | pkg2: Dir({ 549 | 'package.json': File(pkg2), 550 | }), 551 | pkg3: Dir({ 552 | '.babelrc': File({ 553 | presets: ['@babel/preset-env'], 554 | }), 555 | 'package.json': File(pkg3), 556 | }), 557 | pkg4: Dir({ 558 | 'package.json': File(pkg4), 559 | }), 560 | }), 561 | }); 562 | 563 | const [config1, config2, config3, config4] = await Promise.all([ 564 | pectinBabelrc(pkg1, path.join(cwd, 'packages', 'pkg1')), 565 | pectinBabelrc(pkg2, path.join(cwd, 'packages', 'pkg2')), 566 | pectinBabelrc(pkg3, path.join(cwd, 'packages', 'pkg3')), 567 | pectinBabelrc(pkg4, path.join(cwd, 'packages', 'pkg4'), { format: 'esm' }), 568 | ]); 569 | 570 | expect(config1).toMatchInlineSnapshot(` 571 | Object { 572 | "babelrc": false, 573 | "cwd": "/packages/pkg1", 574 | "exclude": "node_modules/**", 575 | "extensions": Array [ 576 | ".js", 577 | ".jsx", 578 | ".es6", 579 | ".es", 580 | ".mjs", 581 | ".ts", 582 | ".tsx", 583 | ], 584 | "plugins": Array [ 585 | "@babel/plugin-proposal-object-rest-spread", 586 | ], 587 | "presets": Array [ 588 | "@babel/preset-env", 589 | ], 590 | } 591 | `); 592 | expect(config2).toMatchInlineSnapshot(` 593 | Object { 594 | "babelrc": false, 595 | "cwd": "/packages/pkg2", 596 | "exclude": "node_modules/**", 597 | "extensions": Array [ 598 | ".js", 599 | ".jsx", 600 | ".es6", 601 | ".es", 602 | ".mjs", 603 | ".ts", 604 | ".tsx", 605 | ], 606 | "plugins": Array [ 607 | "lodash", 608 | ], 609 | "presets": Array [ 610 | "@babel/env", 611 | ], 612 | } 613 | `); 614 | expect(config3).toMatchInlineSnapshot(` 615 | Object { 616 | "babelrc": false, 617 | "cwd": "/packages/pkg3", 618 | "exclude": "node_modules/**", 619 | "extensions": Array [ 620 | ".js", 621 | ".jsx", 622 | ".es6", 623 | ".es", 624 | ".mjs", 625 | ".ts", 626 | ".tsx", 627 | ], 628 | "plugins": Array [ 629 | Array [ 630 | "@babel/plugin-transform-runtime", 631 | Object { 632 | "useESModules": false, 633 | "version": "${BABEL_RUNTIME_DEFAULT_VERSION}", 634 | }, 635 | ], 636 | ], 637 | "presets": Array [ 638 | "@babel/preset-env", 639 | ], 640 | "runtimeHelpers": true, 641 | } 642 | `); 643 | expect(config4).toMatchInlineSnapshot(` 644 | Object { 645 | "babelrc": false, 646 | "cwd": "/packages/pkg4", 647 | "exclude": "node_modules/**", 648 | "extensions": Array [ 649 | ".js", 650 | ".jsx", 651 | ".es6", 652 | ".es", 653 | ".mjs", 654 | ".ts", 655 | ".tsx", 656 | ], 657 | "plugins": Array [ 658 | "@babel/plugin-syntax-dynamic-import", 659 | "@babel/plugin-proposal-object-rest-spread", 660 | Array [ 661 | "@babel/plugin-transform-runtime", 662 | Object { 663 | "useESModules": true, 664 | "version": "${BABEL_RUNTIME_DEFAULT_VERSION}", 665 | }, 666 | ], 667 | ], 668 | "presets": Array [ 669 | "@babel/preset-env", 670 | ], 671 | "runtimeHelpers": true, 672 | } 673 | `); 674 | }); 675 | }); 676 | --------------------------------------------------------------------------------