├── .babelrc ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .prettierrc ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── checkpoint ├── LICENSE ├── README.md ├── dist-node │ ├── index.bin.js │ ├── index.js │ └── index.js.map ├── dist-src │ ├── commands │ │ └── build.js │ ├── config.js │ ├── constants.js │ ├── errors.js │ ├── index.js │ ├── reporters │ │ ├── base-reporter.js │ │ ├── console │ │ │ ├── console-reporter.js │ │ │ ├── helpers │ │ │ │ └── tree-helper.js │ │ │ ├── progress-bar.js │ │ │ ├── spinner-progress.js │ │ │ └── util.js │ │ ├── format.js │ │ ├── index.js │ │ ├── json-reporter.js │ │ ├── lang │ │ │ ├── en.js │ │ │ └── index.js │ │ ├── noop-reporter.js │ │ └── types.js │ ├── types.js │ └── util │ │ ├── babel-plugin-import-rewrite.js │ │ ├── babel-validate-specifier.js │ │ ├── blocking-queue.js │ │ ├── child.js │ │ ├── conversion.js │ │ ├── execute-lifecycle-script.js │ │ ├── fix-cmd-win-slashes.js │ │ ├── fs-normalized.js │ │ ├── fs.js │ │ ├── map.js │ │ ├── misc.js │ │ ├── normalize-manifest │ │ ├── fix.js │ │ ├── for-publish.js │ │ ├── index.js │ │ ├── infer-license.js │ │ ├── licenses.js │ │ ├── typos.js │ │ ├── util.js │ │ └── validate.js │ │ ├── promise.js │ │ └── signal-handler.js ├── dist-types │ ├── commands │ │ └── build.d.ts │ ├── config.d.ts │ ├── constants.d.ts │ ├── errors.d.ts │ ├── index.d.ts │ ├── reporters │ │ ├── base-reporter.d.ts │ │ ├── console │ │ │ ├── console-reporter.d.ts │ │ │ ├── helpers │ │ │ │ └── tree-helper.d.ts │ │ │ ├── progress-bar.d.ts │ │ │ ├── spinner-progress.d.ts │ │ │ └── util.d.ts │ │ ├── format.d.ts │ │ ├── index.d.ts │ │ ├── json-reporter.d.ts │ │ ├── lang │ │ │ ├── en.d.ts │ │ │ └── index.d.ts │ │ ├── noop-reporter.d.ts │ │ └── types.d.ts │ ├── types.d.ts │ └── util │ │ ├── babel-plugin-import-rewrite.d.ts │ │ ├── babel-validate-specifier.d.ts │ │ ├── blocking-queue.d.ts │ │ ├── child.d.ts │ │ ├── conversion.d.ts │ │ ├── execute-lifecycle-script.d.ts │ │ ├── fix-cmd-win-slashes.d.ts │ │ ├── fs-normalized.d.ts │ │ ├── fs.d.ts │ │ ├── map.d.ts │ │ ├── misc.d.ts │ │ ├── normalize-manifest │ │ ├── fix.d.ts │ │ ├── for-publish.d.ts │ │ ├── index.d.ts │ │ ├── infer-license.d.ts │ │ ├── licenses.d.ts │ │ ├── typos.d.ts │ │ ├── util.d.ts │ │ └── validate.d.ts │ │ ├── promise.d.ts │ │ └── signal-handler.d.ts └── package.json ├── package-lock.json ├── package.json ├── src ├── commands │ └── build.ts ├── config.ts ├── constants.ts ├── errors.ts ├── index.ts ├── reporters │ ├── base-reporter.ts │ ├── console │ │ ├── console-reporter.ts │ │ ├── helpers │ │ │ └── tree-helper.ts │ │ ├── progress-bar.ts │ │ ├── spinner-progress.ts │ │ └── util.ts │ ├── format.ts │ ├── index.ts │ ├── json-reporter.ts │ ├── lang │ │ ├── en.ts │ │ └── index.ts │ ├── noop-reporter.ts │ └── types.ts ├── types.ts └── util │ ├── babel-plugin-import-rewrite.ts │ ├── babel-validate-specifier.ts │ ├── blocking-queue.ts │ ├── child.ts │ ├── conversion.ts │ ├── execute-lifecycle-script.ts │ ├── fix-cmd-win-slashes.ts │ ├── fs-normalized.ts │ ├── fs.ts │ ├── map.ts │ ├── misc.ts │ ├── normalize-manifest │ ├── fix.ts │ ├── for-publish.ts │ ├── index.ts │ ├── infer-license.ts │ ├── licenses.ts │ ├── typos.ts │ ├── util.ts │ └── validate.ts │ ├── promise.ts │ └── signal-handler.ts └── tsconfig.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [], 3 | "plugins": [ 4 | "@babel/plugin-proposal-class-properties" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | To create a new issue or search existing discussions, start here: 2 | 👉 **https://www.pika.dev/packages/@pika/pack/discuss** 👈 3 | 4 | All new issues created directly through GitHub will be closed. 5 | Learn more: https://www.pika.dev/discussions 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-node/ 2 | local/ 3 | .DS_Store 4 | /pkg 5 | /lib 6 | /lib-legacy 7 | /node_modules 8 | *.log 9 | /.nyc_output 10 | /coverage 11 | /dist-* 12 | /dist 13 | /dist-debug 14 | /artifacts 15 | /updates 16 | /resources/winsetup/generated.wxs 17 | /resources/winsetup/obj 18 | /resources/winsetup/bin 19 | /resources/win-chocolatey/tools/chocolateyinstall.ps1 20 | .vs 21 | *.msi 22 | *.nupkg 23 | test/fixtures/**/.fbkpm 24 | /tmp/ 25 | /__tests__/fixtures/**/_* 26 | /__tests__/fixtures/request-cache/GET/localhost/.bin 27 | .idea 28 | .pika-meta 29 | .pnp.js 30 | .pnp 31 | /packages/lockfile/index.js 32 | .vscode/ 33 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "bracketSpacing": false, 5 | "printWidth": 120 6 | } 7 | -------------------------------------------------------------------------------- /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 fkschott@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 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to @pika/pack 2 | 3 | All contributions are welcome! 4 | 5 | ## Building the Project 6 | 7 | It is very cool being able to use @pika/pack to build @pika/pack. Unfortunately, npm doesn't make it easy to install a package as a dependency of itself. So to get around this, we keep a `checkpoint/` folder in the project which is a checkpoint build of a working @pika/pack. We then use this to build our package in development. 8 | 9 | ``` 10 | git clone https://github.com/pikapkg/pack.git 11 | npm install 12 | npm run build 13 | ``` 14 | 15 | ## Testing the Project 16 | 17 | Writing unit tests for the project is still TODO. I know, I know, I'm more embarassed than anyone. 18 | 19 | The good news is that we have several example projects, including @pika/pack itself. First I'd like to get Travis running builds of all of our example projects with the PR'd version of @pika/pack. Until then, our only automated test is to use @pika/pack to build @pika/pack. 20 | 21 | ``` 22 | npm t 23 | ``` 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | """ 2 | The MIT License 3 | 4 | Copyright (c) 2019 Fred K. Schott 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | "" 24 | 25 | 26 | This license applies to parts of @pika/pack originating from the 27 | https://github.com/sindresorhus/np repository: 28 | 29 | """ 30 | MIT License 31 | 32 | Copyright (c) Sindre Sorhus (sindresorhus.com) 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 35 | 36 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 37 | 38 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 39 | """ 40 | 41 | 42 | This license applies to parts of @pika/pack originating from the 43 | https://github.com/yarnpkg/yarn repository: 44 | 45 | """ 46 | BSD 2-Clause License 47 | 48 | For Yarn software 49 | 50 | Copyright (c) 2016-present, Yarn Contributors. All rights reserved. 51 | 52 | Redistribution and use in source and binary forms, with or without modification, 53 | are permitted provided that the following conditions are met: 54 | 55 | * Redistributions of source code must retain the above copyright notice, this 56 | list of conditions and the following disclaimer. 57 | 58 | * Redistributions in binary form must reproduce the above copyright notice, 59 | this list of conditions and the following disclaimer in the documentation 60 | and/or other materials provided with the distribution. 61 | 62 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 63 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 64 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 65 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 66 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 67 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 68 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 69 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 70 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 71 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 72 | 73 | """ 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Logo 3 |

4 | 5 |

6 | @pika/pack • npm package building, reimagined. 7 |

8 | 9 |

10 | Demo 11 |

12 | 13 | 14 | ## @pika/pack helps you build amazing packages without the hassle: 15 | 16 | - **Simple**  ⚡️  Use pre-configured plugins to build your package for you. 17 | - **Flexible**  🏋️‍♀️  Choose plugins and optimizations to match your needs. 18 | - **Holistic**  ⚛️  Let us build the entire package... *including package.json.* 19 | 20 | 21 | ## Quickstart 22 | 23 | ```bash 24 | npx @pika/pack # Run once. 25 | npm install --dev @pika/pack # Or, run multiple times using "pika pack" in any package.json scripts 26 | ``` 27 | 28 | 😎🆒! So now what? If you run `pika build` with an empty pipeline, you'll get an empty package build. **@pika/pack** lets you connect pre-configured plugins to build and optimize your package for you. Plugins wrap already-popular tools like Babel and Rollup with npm-optimized config options, removing the need to fiddle with much (if any) configuration yourself. You even get a generated package.json manifest configured for you ***automatically***. 29 | 30 | ### 1. Create a project pipeline out of simple, pluggable builders. 31 | 32 | ```js 33 | // Before: Your top-level package.json manifest: 34 | { 35 | "name": "simple-package", 36 | "version": "1.0.0", 37 | "@pika/pack": { 38 | "pipeline": [ 39 | ["@pika/plugin-standard-pkg", {"exclude": ["__tests__/**/*"]}], 40 | ["@pika/plugin-build-node"], 41 | ["@pika/plugin-build-web"], 42 | ["@pika/plugin-build-types"] 43 | ] 44 | } 45 | } 46 | ``` 47 | 48 | Builders are simple, single-purpose build plugins defined in your `package.json`. For example, `@pika/plugin-build-node` & `@pika/plugin-build-web` build your package for those different environments. Other, more interesting builders can bundle your web build for [unpkg](https://unpkg.com), generate TypeScript definitions from your JavaScript, addon a standard CLI wrapper for Node.js builds, and even compile non-JS languages to WASM (with JS bindings added). 49 | 50 | ### 2. Builders handle everything, including package configuration. 51 | 52 | ```js 53 | // After: your built "pkg/" package.json manifest: 54 | { 55 | "name": "simple-package", 56 | "version": "1.0.0", 57 | // Multiple distributions, built & configured automatically: 58 | "esnext": "dist-src/index.js", 59 | "main": "dist-node/index.js", 60 | "module": "dist-web/index.js", 61 | "types": "dist-types/index.d.ts", 62 | // With sensible package defaults: 63 | "sideEffects": false, 64 | "files": ["dist-*/", "assets/", "bin/"] 65 | } 66 | ``` 67 | 68 | This is all possible because **@pika/pack** builds your entire package: code, assets, and even package.json manifest. By building the entire package, you end up with a fully-built `pkg/` directory, ready to publish. Entry points like "main", "module", "umd:main", "types", "unpkg", "files", and even advanced options like "sideEffects" are all handled by your build pipeline. 69 | 70 | 71 | ## Build Plugins 72 | 73 | **[Check out the full list](https://github.com/pikapkg/builders)** of official & community-written @pika/pack plugins! 74 | 75 | 76 | ## Lerna Support 77 | 78 | Curious about integrating @pika/pack with Lerna? Our official collection of plugins is a Lerna repo that uses @pika/pack to build each package! [Check it out](https://github.com/pikapkg/builders) to see how easy it is to use the two tools together. 79 | -------------------------------------------------------------------------------- /checkpoint/LICENSE: -------------------------------------------------------------------------------- 1 | """ 2 | The MIT License 3 | 4 | Copyright (c) 2019 Fred K. Schott 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | "" 24 | 25 | 26 | This license applies to parts of @pika/pack originating from the 27 | https://github.com/sindresorhus/np repository: 28 | 29 | """ 30 | MIT License 31 | 32 | Copyright (c) Sindre Sorhus (sindresorhus.com) 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 35 | 36 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 37 | 38 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 39 | """ 40 | 41 | 42 | This license applies to parts of @pika/pack originating from the 43 | https://github.com/yarnpkg/yarn repository: 44 | 45 | """ 46 | BSD 2-Clause License 47 | 48 | For Yarn software 49 | 50 | Copyright (c) 2016-present, Yarn Contributors. All rights reserved. 51 | 52 | Redistribution and use in source and binary forms, with or without modification, 53 | are permitted provided that the following conditions are met: 54 | 55 | * Redistributions of source code must retain the above copyright notice, this 56 | list of conditions and the following disclaimer. 57 | 58 | * Redistributions in binary form must reproduce the above copyright notice, 59 | this list of conditions and the following disclaimer in the documentation 60 | and/or other materials provided with the distribution. 61 | 62 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 63 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 64 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 65 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 66 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 67 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 68 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 69 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 70 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 71 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 72 | 73 | """ 74 | -------------------------------------------------------------------------------- /checkpoint/README.md: -------------------------------------------------------------------------------- 1 |

2 | Logo 3 |

4 | 5 |

6 | @pika/pack • npm package building, reimagined. 7 |

8 | 9 |

10 | Demo 11 |

12 | 13 | 14 | ## @pika/pack helps you build amazing packages without the hassle: 15 | 16 | - **Simple**  ⚡️  Use pre-configured plugins to build your package for you. 17 | - **Flexible**  🏋️‍♀️  Choose plugins and optimizations to match your needs. 18 | - **Holistic**  ⚛️  Let us build the entire package... *including package.json.* 19 | 20 | 21 | ## Quickstart 22 | 23 | Getting started is easy: 24 | 25 | ```js 26 | // 1. Install it! 27 | $ npm install -g @pika/pack 28 | // 2. Add this to your package.json manifest: 29 | "@pika/pack": { 30 | "pipeline": [] 31 | } 32 | // 3. Run it! 33 | $ pack build 34 | ``` 35 | 36 | ### 😎 🆒 37 | 38 | So now what? If you run `pack build` with an empty pipeline, you'll get an empty package build. **@pika/pack** lets you connect pre-configured plugins to build and optimize your package for you. Plugins wrap already-popular tools like Babel and Rollup with npm-optimized config options, removing the need to fiddle with much (if any) configuration yourself. You even get a generated package.json manifest configured for you ***automatically***. 39 | 40 | ### 1. Create a project pipeline out of simple, pluggable builders. 41 | 42 | ```js 43 | // Before: Your top-level package.json manifest: 44 | { 45 | "name": "simple-package", 46 | "version": "1.0.0", 47 | "@pika/pack": { 48 | "pipeline": [ 49 | ["@pika/plugin-standard-pkg", {"exclude": ["__tests__/**/*"]}], 50 | ["@pika/plugin-build-node"], 51 | ["@pika/plugin-build-web"], 52 | ["@pika/plugin-build-types"] 53 | ] 54 | } 55 | } 56 | ``` 57 | 58 | Builders are simple, single-purpose build plugins defined in your `package.json`. For example, `@pika/plugin-build-node` & `@pika/plugin-build-web` build your package for those different environments. Other, more interesting builders can bundle your web build for [unpkg](https://unpkg.com), generate TypeScript definitions from your JavaScript, addon a standard CLI wrapper for Node.js builds, and even compile non-JS languages to WASM (with JS bindings added). 59 | 60 | ### 2. Builders handle everything, including package configuration. 61 | 62 | ```js 63 | // After: your built "pkg/" package.json manifest: 64 | { 65 | "name": "simple-package", 66 | "version": "1.0.0", 67 | // Multiple distributions, built & configured automatically: 68 | "esnext": "dist-src/index.js", 69 | "main": "dist-node/index.js", 70 | "module": "dist-web/index.js", 71 | "types": "dist-types/index.d.ts", 72 | // With sensible package defaults: 73 | "sideEffects": false, 74 | "files": ["dist-*/", "assets/", "bin/"] 75 | } 76 | ``` 77 | 78 | This is all possible because **@pika/pack** builds your entire package: code, assets, and even package.json manifest. By building the entire package, you end up with a fully-built `pkg/` directory, ready to publish. Entry points like "main", "module", "umd:main", "types", "unpkg", "files", and even advanced options like "sideEffects" are all handled by your build pipeline. 79 | 80 | 81 | ## Build Plugins 82 | 83 | **[Check out the full list](https://github.com/pikapkg/builders)** of official & community-written @pika/pack plugins! 84 | 85 | 86 | ## Lerna Support 87 | 88 | Curious about integrating @pika/pack with Lerna? Our official collection of plugins is a Lerna repo that uses @pika/pack to build each package! [Check it out](https://github.com/pikapkg/builders) to see how easy it is to use the two tools together. 89 | -------------------------------------------------------------------------------- /checkpoint/dist-node/index.bin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | const ver = process.versions.node; 5 | const majorVer = parseInt(ver.split('.')[0], 10); 6 | 7 | if (majorVer < 8) { 8 | console.error('Node version ' + ver + ' is not supported, please use Node.js 8.0 or higher.'); 9 | process.exit(1); 10 | } 11 | 12 | let hasBundled = true 13 | 14 | try { 15 | require.resolve('./index.bundled.js'); 16 | } catch(err) { 17 | // We don't have/need this on legacy builds and dev builds 18 | // If an error happens here, throw it, that means no Node.js distribution exists at all. 19 | hasBundled = false; 20 | } 21 | 22 | const cli = !hasBundled ? require('../') : require('./index.bundled.js'); 23 | 24 | if (cli.autoRun) { 25 | return; 26 | } 27 | 28 | const run = cli.run || cli.cli || cli.default; 29 | run(process.argv).catch(function (error) { 30 | console.error(error.stack || error.message || error); 31 | process.exitCode = 1; 32 | }); 33 | -------------------------------------------------------------------------------- /checkpoint/dist-src/config.js: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as constants from './constants.js'; 3 | import { MessageError } from '@pika/types'; 4 | import * as fs from './util/fs.js'; 5 | import normalizeManifest from './util/normalize-manifest/index.js'; 6 | import executeLifecycleScript from './util/execute-lifecycle-script.js'; 7 | import importFrom from 'import-from'; 8 | ; 9 | ; 10 | export default class Config { 11 | constructor(reporter, cwd, flags) { 12 | this.reporter = reporter; 13 | // Ensure the cwd is always an absolute path. 14 | this.cwd = path.resolve(cwd || process.cwd()); 15 | this.flags = flags; 16 | } 17 | async loadPackageManifest() { 18 | const loc = path.join(this.cwd, constants.NODE_PACKAGE_JSON); 19 | if (await fs.exists(loc)) { 20 | const info = await this.readJson(loc, fs.readJsonAndFile); 21 | this._manifest = { ...info.object }; 22 | this.manifest = await normalizeManifest(info.object, this.cwd, this, true); 23 | return this.manifest; 24 | } 25 | else { 26 | return null; 27 | } 28 | } 29 | readJson(loc, factory = fs.readJson) { 30 | try { 31 | return factory(loc); 32 | } 33 | catch (err) { 34 | if (err instanceof SyntaxError) { 35 | throw new MessageError(this.reporter.lang('jsonError', loc, err.message)); 36 | } 37 | else { 38 | throw err; 39 | } 40 | } 41 | } 42 | async getDistributions() { 43 | const raw = this.manifest[`@pika/pack`] || {}; 44 | const override = this.flags.pipeline && JSON.parse(this.flags.pipeline); 45 | const cwd = this.cwd; 46 | function cleanRawDistObject(rawVal) { 47 | if (Array.isArray(rawVal)) { 48 | let importStr = (rawVal[0].startsWith('./') || rawVal[0].startsWith('../')) ? path.join(cwd, rawVal[0]) : rawVal[0]; 49 | return [{ ...importFrom(cwd, importStr), name: rawVal[0] }, rawVal[1] || {}]; 50 | } 51 | if (typeof rawVal === 'string') { 52 | return [{ build: ({ cwd }) => { 53 | return executeLifecycleScript({ 54 | // config: this, 55 | args: [], 56 | cwd, 57 | cmd: rawVal, 58 | isInteractive: false 59 | }); 60 | } }, {}]; 61 | } 62 | if (!rawVal) { 63 | throw new Error('Cannot be false'); 64 | } 65 | return false; 66 | } 67 | const pipeline = override || raw.pipeline || []; 68 | return pipeline.map(cleanRawDistObject).filter(Boolean); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /checkpoint/dist-src/constants.js: -------------------------------------------------------------------------------- 1 | // import os from 'os'; 2 | // import * as path from 'path'; 3 | // import userHome from './util/user-home-dir.js'; 4 | // import {getCacheDir, getConfigDir, getDataDir} from './util/user-dirs.js'; 5 | export const DEPENDENCY_TYPES = ['devDependencies', 'dependencies', 'legacyDependencies']; 6 | // export const OWNED_DEPENDENCY_TYPES = ['devDependencies', 'dependencies', 'legacyDependencies']; 7 | export const RESOLUTIONS = 'resolutions'; 8 | export const MANIFEST_FIELDS = [RESOLUTIONS, ...DEPENDENCY_TYPES]; 9 | export const SUPPORTED_NODE_VERSIONS = '>=8.5.0'; 10 | // export const PIKA_REGISTRY = 'https://registry.npmjs.org'; 11 | // export const NPM_REGISTRY_RE = /https?:\/\/registry\.npmjs\.org/g; 12 | // export const PIKA_DOCS = 'https://yarnpkg.com/en/docs/cli/'; 13 | // export const PIKA_INSTALLER_SH = 'https://yarnpkg.com/install.sh'; 14 | // export const PIKA_INSTALLER_MSI = 'https://yarnpkg.com/latest.msi'; 15 | // export const SELF_UPDATE_VERSION_URL = 'https://www.pikapkg.com/downloads/latest-version'; 16 | // // cache version, bump whenever we make backwards incompatible changes 17 | // export const CACHE_VERSION = 3; 18 | // // lockfile version, bump whenever we make backwards incompatible changes 19 | // export const LOCKFILE_VERSION = 1; 20 | // // max amount of network requests to perform concurrently 21 | // export const NETWORK_CONCURRENCY = 8; 22 | // // HTTP timeout used when downloading packages 23 | // export const NETWORK_TIMEOUT = 30 * 1000; // in milliseconds 24 | // // max amount of child processes to execute concurrently 25 | export const CHILD_CONCURRENCY = 5; 26 | // export const REQUIRED_PACKAGE_KEYS = ['name', 'version', '_uid']; 27 | // function getPreferredCacheDirectories(): Array { 28 | // const preferredCacheDirectories = [getCacheDir()]; 29 | // if (process.getuid) { 30 | // // $FlowFixMe: process.getuid exists, dammit 31 | // preferredCacheDirectories.push(path.join(os.tmpdir(), `.pika-cache-${process.getuid()}`)); 32 | // } 33 | // preferredCacheDirectories.push(path.join(os.tmpdir(), `.pika-cache`)); 34 | // return preferredCacheDirectories; 35 | // } 36 | // export const PREFERRED_MODULE_CACHE_DIRECTORIES = getPreferredCacheDirectories(); 37 | // export const CONFIG_DIRECTORY = getConfigDir(); 38 | // export const DATA_DIRECTORY = getDataDir(); 39 | // export const LINK_REGISTRY_DIRECTORY = path.join(DATA_DIRECTORY, 'link'); 40 | // export const GLOBAL_MODULE_DIRECTORY = path.join(DATA_DIRECTORY, 'global'); 41 | // export const NODE_BIN_PATH = process.execPath; 42 | export const NODE_MODULES_FOLDER = 'node_modules'; 43 | export const NODE_PACKAGE_JSON = 'package.json'; 44 | // export const PNP_FILENAME = '.pnp'; 45 | // export const POSIX_GLOBAL_PREFIX = `${process.env.DESTDIR || ''}/usr/local`; 46 | // export const FALLBACK_GLOBAL_PREFIX = path.join(userHome, '.pika'); 47 | // export const META_FOLDER = '.pika-meta'; 48 | // export const INTEGRITY_FILENAME = '.pika-integrity'; 49 | // export const LOCKFILE_FILENAME = 'pika.lock'; 50 | // export const LEGACY_LOCKFILE_FILENAME = 'yarn.lock'; 51 | // export const METADATA_FILENAME = '.pika-metadata.json'; 52 | // export const TARBALL_FILENAME = '.pika-tarball.tgz'; 53 | // export const CLEAN_FILENAME = '.pikaclean'; 54 | // export const NPM_LOCK_FILENAME = 'package-lock.json'; 55 | // export const NPM_SHRINKWRAP_FILENAME = 'npm-shrinkwrap.json'; 56 | export const DEFAULT_INDENT = ' '; 57 | // export const SINGLE_INSTANCE_PORT = 31997; 58 | // export const SINGLE_INSTANCE_FILENAME = '.pika-single-instance'; 59 | export const ENV_PATH_KEY = getPathKey(process.platform, process.env); 60 | export function getPathKey(platform, env) { 61 | let pathKey = 'PATH'; 62 | // windows calls its path "Path" usually, but this is not guaranteed. 63 | if (platform === 'win32') { 64 | pathKey = 'Path'; 65 | for (const key in env) { 66 | if (key.toLowerCase() === 'path') { 67 | pathKey = key; 68 | } 69 | } 70 | } 71 | return pathKey; 72 | } 73 | // export const VERSION_COLOR_SCHEME: {[key: string]: VersionColor} = { 74 | // major: 'red', 75 | // premajor: 'red', 76 | // minor: 'yellow', 77 | // preminor: 'yellow', 78 | // patch: 'green', 79 | // prepatch: 'green', 80 | // prerelease: 'red', 81 | // unchanged: 'white', 82 | // unknown: 'red', 83 | // }; 84 | // export type VersionColor = 'red' | 'yellow' | 'green' | 'white'; 85 | // export type RequestHint = 'dev' | 'optional' | 'resolution' | 'workspaces'; 86 | -------------------------------------------------------------------------------- /checkpoint/dist-src/errors.js: -------------------------------------------------------------------------------- 1 | import { MessageError } from '@pika/types'; 2 | export class ProcessSpawnError extends MessageError { 3 | constructor(msg, code, process) { 4 | super(msg); 5 | this.code = code; 6 | this.process = process; 7 | } 8 | } 9 | export class SecurityError extends MessageError { 10 | } 11 | export class ProcessTermError extends MessageError { 12 | } 13 | export class ResponseError extends Error { 14 | constructor(msg, responseCode) { 15 | super(msg); 16 | this.responseCode = responseCode; 17 | } 18 | } 19 | export class OneTimePasswordError extends Error { 20 | } 21 | -------------------------------------------------------------------------------- /checkpoint/dist-src/index.js: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import chalk from 'chalk'; 3 | import * as fs from 'fs'; 4 | import invariant from 'invariant'; 5 | import loudRejection from 'loud-rejection'; 6 | import { ConsoleReporter, JSONReporter } from './reporters/index.js'; 7 | import * as buildCommand from './commands/build.js'; 8 | import { MessageError } from '@pika/types'; 9 | import Config from './config.js'; 10 | import handleSignals from './util/signal-handler.js'; 11 | import { boolifyWithDefault } from './util/conversion.js'; 12 | import map from './util/map.js'; 13 | import stripBOM from 'strip-bom'; 14 | import uri2path from 'file-uri-to-path'; 15 | import yargs from 'yargs-parser'; 16 | // @ts-ignore 17 | const currentFilename = uri2path(import.meta.url); 18 | function getVersion() { 19 | const packageJsonContent = fs.readFileSync(path.resolve(currentFilename, '../../package.json'), { encoding: 'utf-8' }); 20 | const { version } = map(JSON.parse(stripBOM(packageJsonContent))); 21 | return version; 22 | } 23 | function printHelp() { 24 | console.log(` 25 | ${chalk.bold(`@pika/pack`)} - Build npm packages without the mess. 26 | ${chalk.bold('Options:')} 27 | --cwd Set the current working directory. 28 | --out Set the output directory. Defaults to "pkg/". 29 | --pipeline Set a build pipeline via JSON string. 30 | --force Continue with the build when a build plugin fails or throws an exception. 31 | --json Log output as JSON. 32 | --verbose Log additional debugging information. 33 | --silent Log almost nothing. 34 | --help Print help. 35 | --version, -v Print version. 36 | `.trim()); 37 | } 38 | export async function cli(args) { 39 | const version = getVersion(); 40 | loudRejection(); 41 | handleSignals(); 42 | // Handle special flags 43 | if (args.find(arg => arg === '--version' || arg === '-v')) { 44 | console.log(version.trim()); 45 | process.exitCode = 0; 46 | return; 47 | } 48 | if (args.find(arg => arg === '--help')) { 49 | printHelp(); 50 | process.exitCode = 0; 51 | return; 52 | } 53 | // Handle the legacy CLI interface 54 | if (args[2] === 'publish') { 55 | console.log(`The publish flow has moved to the @pika/cli package (included with this package). 56 | Update your publish script to: ${chalk.bold('pika publish [flags]')} 57 | `); 58 | process.exitCode = 1; 59 | return; 60 | } 61 | if (args[2] === 'build') { 62 | console.log(chalk.yellow(`Note: This CLI was recently deprecated. Update your build script to: ${chalk.bold('pika build [flags]')}`)); 63 | args.splice(2, 1); 64 | } 65 | const flags = yargs(args); 66 | const cwd = flags.cwd || process.cwd(); 67 | const Reporter = flags.json ? JSONReporter : ConsoleReporter; 68 | const reporter = new Reporter({ 69 | emoji: true, 70 | verbose: flags.verbose, 71 | isSilent: boolifyWithDefault(process.env.PIKA_SILENT, false) || flags.silent, 72 | }); 73 | const exit = (exitCode = 0) => { 74 | process.exitCode = exitCode; 75 | reporter.close(); 76 | }; 77 | const command = buildCommand; 78 | reporter.initPeakMemoryCounter(); 79 | const outputWrapperEnabled = boolifyWithDefault(process.env.PIKA_WRAP_OUTPUT, true); 80 | const shouldWrapOutput = outputWrapperEnabled && !flags.json && command.hasWrapper(); 81 | if (shouldWrapOutput) { 82 | reporter.header({ name: '@pika/pack', version }); 83 | } 84 | const run = () => { 85 | invariant(command, 'missing command'); 86 | return command.run(config, reporter, flags, args).then(exitCode => { 87 | if (shouldWrapOutput) { 88 | reporter.footer(false); 89 | } 90 | return exitCode; 91 | }); 92 | }; 93 | function onUnexpectedError(err) { 94 | function indent(str) { 95 | return '\n ' + str.trim().split('\n').join('\n '); 96 | } 97 | const log = []; 98 | log.push(`Arguments: ${indent(process.argv.join(' '))}`); 99 | log.push(`PATH: ${indent(process.env.PATH || 'undefined')}`); 100 | log.push(`Pika version: ${indent(version)}`); 101 | log.push(`Node version: ${indent(process.versions.node)}`); 102 | log.push(`Platform: ${indent(process.platform + ' ' + process.arch)}`); 103 | log.push(`Trace: ${indent(err.stack)}`); 104 | reporter.error(reporter.lang('unexpectedError', err.message)); 105 | } 106 | const config = new Config(reporter, cwd, flags); 107 | await config.loadPackageManifest(); 108 | try { 109 | // option "no-progress" stored in pika config 110 | const noProgressConfig = false; //config.registries.pika.getOption('no-progress'); 111 | if (noProgressConfig) { 112 | reporter.disableProgress(); 113 | } 114 | // verbose logs outputs process.uptime() with this line we can sync uptime to absolute time on the computer 115 | reporter.verbose(`current time: ${new Date().toISOString()}`); 116 | return run().then(exit); 117 | } 118 | catch (err) { 119 | reporter.verbose(err.stack); 120 | if (err instanceof MessageError) { 121 | reporter.error(err.message); 122 | } 123 | else { 124 | onUnexpectedError(err); 125 | } 126 | return exit(1); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /checkpoint/dist-src/reporters/base-reporter.js: -------------------------------------------------------------------------------- 1 | import { defaultFormatter } from './format.js'; 2 | import * as languages from './lang/index.js'; 3 | import * as isCI from 'is-ci'; 4 | import * as os from 'os'; 5 | import * as util from 'util'; 6 | import { EventEmitter } from 'events'; 7 | export function stringifyLangArgs(args) { 8 | return args.map(function (val) { 9 | if (val != null && val.inspect) { 10 | return val.inspect(); 11 | } 12 | else { 13 | try { 14 | const str = JSON.stringify(val) || val + ''; 15 | // should match all literal line breaks and 16 | // "u001b" that follow an odd number of backslashes and convert them to ESC 17 | // we do this because the JSON.stringify process has escaped these characters 18 | return str 19 | .replace(/((?:^|[^\\])(?:\\{2})*)\\u001[bB]/g, '$1\u001b') 20 | .replace(/[\\]r[\\]n|([\\])?[\\]n/g, (match, precededBacklash) => { 21 | // precededBacklash not null when "\n" is preceded by a backlash ("\\n") 22 | // match will be "\\n" and we don't replace it with os.EOL 23 | return precededBacklash ? match : os.EOL; 24 | }); 25 | } 26 | catch (e) { 27 | return util.inspect(val); 28 | } 29 | } 30 | }); 31 | } 32 | export default class BaseReporter { 33 | constructor(opts = {}) { 34 | const lang = 'en'; 35 | this.language = lang; 36 | this.stdout = opts.stdout || process.stdout; 37 | this.stderr = opts.stderr || process.stderr; 38 | this.stdin = opts.stdin || this._getStandardInput(); 39 | this.emoji = !!opts.emoji; 40 | this.nonInteractive = !!opts.nonInteractive; 41 | this.noProgress = !!opts.noProgress || isCI; 42 | this.isVerbose = !!opts.verbose; 43 | // @ts-ignore 44 | this.isTTY = this.stdout.isTTY; 45 | this.peakMemory = 0; 46 | this.startTime = Date.now(); 47 | this.format = defaultFormatter; 48 | } 49 | lang(key, ...args) { 50 | const msg = languages[this.language][key] || languages.en[key]; 51 | if (!msg) { 52 | throw new ReferenceError(`No message defined for language key ${key}`); 53 | } 54 | // stringify args 55 | const stringifiedArgs = stringifyLangArgs(args); 56 | // replace $0 placeholders with args 57 | return msg.replace(/\$(\d+)/g, (str, i) => { 58 | return stringifiedArgs[i]; 59 | }); 60 | } 61 | /** 62 | * `stringifyLangArgs` run `JSON.stringify` on strings too causing 63 | * them to appear quoted. This marks them as "raw" and prevents 64 | * the quoting and escaping 65 | */ 66 | rawText(str) { 67 | return { 68 | inspect() { 69 | return str; 70 | }, 71 | }; 72 | } 73 | verbose(msg) { 74 | if (this.isVerbose) { 75 | this._verbose(msg); 76 | } 77 | } 78 | verboseInspect(val) { 79 | if (this.isVerbose) { 80 | this._verboseInspect(val); 81 | } 82 | } 83 | _verbose(msg) { } 84 | _verboseInspect(val) { } 85 | _getStandardInput() { 86 | let standardInput; 87 | // Accessing stdin in a win32 headless process (e.g., Visual Studio) may throw an exception. 88 | try { 89 | standardInput = process.stdin; 90 | } 91 | catch (e) { 92 | console.warn(e.message); 93 | delete process.stdin; 94 | // @ts-ignore 95 | process.stdin = new EventEmitter(); 96 | standardInput = process.stdin; 97 | } 98 | return standardInput; 99 | } 100 | initPeakMemoryCounter() { 101 | this.checkPeakMemory(); 102 | this.peakMemoryInterval = setInterval(() => { 103 | this.checkPeakMemory(); 104 | }, 1000); 105 | // $FlowFixMe: Node's setInterval returns a Timeout, not a Number 106 | this.peakMemoryInterval.unref(); 107 | } 108 | checkPeakMemory() { 109 | const { heapTotal } = process.memoryUsage(); 110 | if (heapTotal > this.peakMemory) { 111 | this.peakMemory = heapTotal; 112 | } 113 | } 114 | close() { 115 | if (this.peakMemoryInterval) { 116 | clearInterval(this.peakMemoryInterval); 117 | this.peakMemoryInterval = null; 118 | } 119 | } 120 | getTotalTime() { 121 | return Date.now() - this.startTime; 122 | } 123 | // TODO 124 | list(key, items, hints) { } 125 | // Outputs basic tree structure to console 126 | tree(key, obj, { force = false } = {}) { } 127 | // called whenever we begin a step in the CLI. 128 | step(current, total, message, emoji) { } 129 | // a error message has been triggered. this however does not always meant an abrupt 130 | // program end. 131 | error(message) { } 132 | // an info message has been triggered. this provides things like stats and diagnostics. 133 | info(message) { } 134 | // a warning message has been triggered. 135 | warn(message) { } 136 | // a success message has been triggered. 137 | success(message) { } 138 | // a simple log message 139 | // TODO: rethink the {force} parameter. In the meantime, please don't use it (cf comments in #4143). 140 | log(message, { force = false } = {}) { } 141 | // a shell command has been executed 142 | command(command) { } 143 | // inspect and pretty-print any value 144 | inspect(value) { } 145 | // the screen shown at the very start of the CLI 146 | header(pkg) { } 147 | // the screen shown at the very end of the CLI 148 | footer(showPeakMemory) { } 149 | // a table structure 150 | table(head, body) { } 151 | // render an activity spinner and return a function that will trigger an update 152 | activity() { 153 | return { 154 | tick(name) { }, 155 | end() { }, 156 | }; 157 | } 158 | // 159 | activitySet(total, workers) { 160 | return { 161 | spinners: Array(workers).fill({ 162 | clear() { }, 163 | setPrefix() { }, 164 | tick() { }, 165 | end() { }, 166 | }), 167 | end() { }, 168 | }; 169 | } 170 | // render a progress bar and return a function which when called will trigger an update 171 | progress(total) { 172 | return function () { }; 173 | } 174 | // utility function to disable progress bar 175 | disableProgress() { 176 | this.noProgress = true; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /checkpoint/dist-src/reporters/console/helpers/tree-helper.js: -------------------------------------------------------------------------------- 1 | // public 2 | export function sortTrees(trees) { 3 | return trees.sort(function (tree1, tree2) { 4 | return tree1.name.localeCompare(tree2.name); 5 | }); 6 | } 7 | export function recurseTree(tree, prefix, recurseFunc) { 8 | const treeLen = tree.length; 9 | const treeEnd = treeLen - 1; 10 | for (let i = 0; i < treeLen; i++) { 11 | const atEnd = i === treeEnd; 12 | recurseFunc(tree[i], prefix + getLastIndentChar(atEnd), prefix + getNextIndentChar(atEnd)); 13 | } 14 | } 15 | export function getFormattedOutput(fmt) { 16 | const item = formatColor(fmt.color, fmt.name, fmt.formatter); 17 | const suffix = getSuffix(fmt.hint, fmt.formatter); 18 | return `${fmt.prefix}─ ${item}${suffix}\n`; 19 | } 20 | function getNextIndentChar(end) { 21 | return end ? ' ' : '│ '; 22 | } 23 | function getLastIndentChar(end) { 24 | return end ? '└' : '├'; 25 | } 26 | function getSuffix(hint, formatter) { 27 | return hint ? ` (${formatter.grey(hint)})` : ''; 28 | } 29 | function formatColor(color, strToFormat, formatter) { 30 | return color ? formatter[color](strToFormat) : strToFormat; 31 | } 32 | -------------------------------------------------------------------------------- /checkpoint/dist-src/reporters/console/progress-bar.js: -------------------------------------------------------------------------------- 1 | import { clearLine, toStartOfLine } from './util.js'; 2 | export default class ProgressBar { 3 | constructor(total, stdout = process.stderr, callback) { 4 | this.stdout = stdout; 5 | this.total = total; 6 | this.chars = ProgressBar.bars[0]; 7 | this.delay = 60; 8 | this.curr = 0; 9 | this._callback = callback; 10 | clearLine(stdout); 11 | } 12 | tick() { 13 | if (this.curr >= this.total) { 14 | return; 15 | } 16 | this.curr++; 17 | // schedule render 18 | if (!this.id) { 19 | this.id = setTimeout(() => this.render(), this.delay); 20 | } 21 | } 22 | cancelTick() { 23 | if (this.id) { 24 | clearTimeout(this.id); 25 | this.id = null; 26 | } 27 | } 28 | stop() { 29 | // "stop" by setting current to end so `tick` becomes noop 30 | this.curr = this.total; 31 | this.cancelTick(); 32 | clearLine(this.stdout); 33 | if (this._callback) { 34 | this._callback(this); 35 | } 36 | } 37 | render() { 38 | // clear throttle 39 | this.cancelTick(); 40 | let ratio = this.curr / this.total; 41 | ratio = Math.min(Math.max(ratio, 0), 1); 42 | // progress without bar 43 | let bar = ` ${this.curr}/${this.total}`; 44 | // calculate size of actual bar 45 | // $FlowFixMe: investigate process.stderr.columns flow error 46 | // @ts-ignore 47 | const availableSpace = Math.max(0, this.stdout.columns - bar.length - 3); 48 | const width = Math.min(this.total, availableSpace); 49 | const completeLength = Math.round(width * ratio); 50 | const complete = this.chars[0].repeat(completeLength); 51 | const incomplete = this.chars[1].repeat(width - completeLength); 52 | bar = `[${complete}${incomplete}]${bar}`; 53 | toStartOfLine(this.stdout); 54 | this.stdout.write(bar); 55 | } 56 | } 57 | ProgressBar.bars = [['#', '-']]; 58 | -------------------------------------------------------------------------------- /checkpoint/dist-src/reporters/console/spinner-progress.js: -------------------------------------------------------------------------------- 1 | import { writeOnNthLine, clearNthLine } from './util.js'; 2 | export default class Spinner { 3 | constructor(stdout = process.stderr, lineNumber = 0) { 4 | this.current = 0; 5 | this.prefix = ''; 6 | this.lineNumber = lineNumber; 7 | this.stdout = stdout; 8 | this.delay = 60; 9 | this.chars = Spinner.spinners[28].split(''); 10 | this.text = ''; 11 | this.id = null; 12 | } 13 | setPrefix(prefix) { 14 | this.prefix = prefix; 15 | } 16 | setText(text) { 17 | this.text = text; 18 | } 19 | start() { 20 | this.current = 0; 21 | this.render(); 22 | } 23 | render() { 24 | if (this.id) { 25 | clearTimeout(this.id); 26 | } 27 | // build line ensuring we don't wrap to the next line 28 | let msg = `${this.prefix}${this.chars[this.current]} ${this.text}`; 29 | // @ts-ignore 30 | const columns = typeof this.stdout.columns === 'number' ? this.stdout.columns : 100; 31 | msg = msg.slice(0, columns); 32 | writeOnNthLine(this.stdout, this.lineNumber, msg); 33 | this.current = ++this.current % this.chars.length; 34 | this.id = setTimeout(() => this.render(), this.delay); 35 | } 36 | stop() { 37 | if (this.id) { 38 | clearTimeout(this.id); 39 | this.id = null; 40 | } 41 | clearNthLine(this.stdout, this.lineNumber); 42 | } 43 | } 44 | Spinner.spinners = [ 45 | '|/-\\', 46 | '⠂-–—–-', 47 | '◐◓◑◒', 48 | '◴◷◶◵', 49 | '◰◳◲◱', 50 | '▖▘▝▗', 51 | '■□▪▫', 52 | '▌▀▐▄', 53 | '▉▊▋▌▍▎▏▎▍▌▋▊▉', 54 | '▁▃▄▅▆▇█▇▆▅▄▃', 55 | '←↖↑↗→↘↓↙', 56 | '┤┘┴└├┌┬┐', 57 | '◢◣◤◥', 58 | '.oO°Oo.', 59 | '.oO@*', 60 | '🌍🌎🌏', 61 | '◡◡ ⊙⊙ ◠◠', 62 | '☱☲☴', 63 | '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏', 64 | '⠋⠙⠚⠞⠖⠦⠴⠲⠳⠓', 65 | '⠄⠆⠇⠋⠙⠸⠰⠠⠰⠸⠙⠋⠇⠆', 66 | '⠋⠙⠚⠒⠂⠂⠒⠲⠴⠦⠖⠒⠐⠐⠒⠓⠋', 67 | '⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠴⠲⠒⠂⠂⠒⠚⠙⠉⠁', 68 | '⠈⠉⠋⠓⠒⠐⠐⠒⠖⠦⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈', 69 | '⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈', 70 | '⢄⢂⢁⡁⡈⡐⡠', 71 | '⢹⢺⢼⣸⣇⡧⡗⡏', 72 | '⣾⣽⣻⢿⡿⣟⣯⣷', 73 | '⠁⠂⠄⡀⢀⠠⠐⠈', 74 | ]; 75 | -------------------------------------------------------------------------------- /checkpoint/dist-src/reporters/console/util.js: -------------------------------------------------------------------------------- 1 | import * as tty from 'tty'; 2 | import * as readline from 'readline'; 3 | import chalk from 'chalk'; 4 | const CLEAR_WHOLE_LINE = 0; 5 | const CLEAR_RIGHT_OF_CURSOR = 1; 6 | export function clearLine(stdout) { 7 | if (!chalk.supportsColor) { 8 | if (stdout instanceof tty.WriteStream) { 9 | if (stdout.columns > 0) { 10 | stdout.write(`\r${' '.repeat(stdout.columns - 1)}`); 11 | } 12 | stdout.write(`\r`); 13 | } 14 | return; 15 | } 16 | readline.clearLine(stdout, CLEAR_WHOLE_LINE); 17 | readline.cursorTo(stdout, 0); 18 | } 19 | export function toStartOfLine(stdout) { 20 | if (!chalk.supportsColor) { 21 | stdout.write('\r'); 22 | return; 23 | } 24 | readline.cursorTo(stdout, 0); 25 | } 26 | export function writeOnNthLine(stdout, n, msg) { 27 | if (!chalk.supportsColor) { 28 | return; 29 | } 30 | if (n == 0) { 31 | readline.cursorTo(stdout, 0); 32 | stdout.write(msg); 33 | readline.clearLine(stdout, CLEAR_RIGHT_OF_CURSOR); 34 | return; 35 | } 36 | readline.cursorTo(stdout, 0); 37 | readline.moveCursor(stdout, 0, -n); 38 | stdout.write(msg); 39 | readline.clearLine(stdout, CLEAR_RIGHT_OF_CURSOR); 40 | readline.cursorTo(stdout, 0); 41 | readline.moveCursor(stdout, 0, n); 42 | } 43 | export function clearNthLine(stdout, n) { 44 | if (!chalk.supportsColor) { 45 | return; 46 | } 47 | if (n == 0) { 48 | clearLine(stdout); 49 | return; 50 | } 51 | readline.cursorTo(stdout, 0); 52 | readline.moveCursor(stdout, 0, -n); 53 | readline.clearLine(stdout, CLEAR_WHOLE_LINE); 54 | readline.moveCursor(stdout, 0, n); 55 | } 56 | -------------------------------------------------------------------------------- /checkpoint/dist-src/reporters/format.js: -------------------------------------------------------------------------------- 1 | function formatFunction(...strs) { 2 | return strs.join(' '); 3 | } 4 | export const defaultFormatter = { 5 | bold: formatFunction, 6 | dim: formatFunction, 7 | italic: formatFunction, 8 | underline: formatFunction, 9 | inverse: formatFunction, 10 | strikethrough: formatFunction, 11 | black: formatFunction, 12 | red: formatFunction, 13 | green: formatFunction, 14 | yellow: formatFunction, 15 | blue: formatFunction, 16 | magenta: formatFunction, 17 | cyan: formatFunction, 18 | white: formatFunction, 19 | gray: formatFunction, 20 | grey: formatFunction, 21 | stripColor: formatFunction, 22 | }; 23 | -------------------------------------------------------------------------------- /checkpoint/dist-src/reporters/index.js: -------------------------------------------------------------------------------- 1 | export { default as ConsoleReporter } from './console/console-reporter'; 2 | export { default as JSONReporter } from './json-reporter'; 3 | export { default as NoopReporter } from './noop-reporter'; 4 | export { default as Reporter } from './base-reporter'; 5 | -------------------------------------------------------------------------------- /checkpoint/dist-src/reporters/json-reporter.js: -------------------------------------------------------------------------------- 1 | import BaseReporter from './base-reporter.js'; 2 | export default class JSONReporter extends BaseReporter { 3 | constructor(opts) { 4 | super(opts); 5 | this._activityId = 0; 6 | this._progressId = 0; 7 | } 8 | _dump(type, data, error) { 9 | let stdout = this.stdout; 10 | if (error) { 11 | stdout = this.stderr; 12 | } 13 | stdout.write(`${JSON.stringify({ type, data })}\n`); 14 | } 15 | _verbose(msg) { 16 | this._dump('verbose', msg); 17 | } 18 | list(type, items, hints) { 19 | this._dump('list', { type, items, hints }); 20 | } 21 | tree(type, trees) { 22 | this._dump('tree', { type, trees }); 23 | } 24 | step(current, total, message) { 25 | this._dump('step', { message, current, total }); 26 | } 27 | inspect(value) { 28 | this._dump('inspect', value); 29 | } 30 | footer(showPeakMemory) { 31 | this._dump('finished', this.getTotalTime()); 32 | } 33 | log(msg) { 34 | this._dump('log', msg); 35 | } 36 | command(msg) { 37 | this._dump('command', msg); 38 | } 39 | table(head, body) { 40 | this._dump('table', { head, body }); 41 | } 42 | success(msg) { 43 | this._dump('success', msg); 44 | } 45 | error(msg) { 46 | this._dump('error', msg, true); 47 | } 48 | warn(msg) { 49 | this._dump('warning', msg, true); 50 | } 51 | info(msg) { 52 | this._dump('info', msg); 53 | } 54 | activitySet(total, workers) { 55 | if (!this.isTTY || this.noProgress) { 56 | return super.activitySet(total, workers); 57 | } 58 | const id = this._activityId++; 59 | this._dump('activitySetStart', { id, total, workers }); 60 | const spinners = []; 61 | for (let i = 0; i < workers; i++) { 62 | let current = 0; 63 | let header = ''; 64 | spinners.push({ 65 | clear() { }, 66 | setPrefix(_current, _header) { 67 | current = _current; 68 | header = _header; 69 | }, 70 | tick: msg => { 71 | this._dump('activitySetTick', { 72 | id, 73 | header, 74 | current, 75 | worker: i, 76 | message: msg, 77 | }); 78 | }, 79 | end() { }, 80 | }); 81 | } 82 | return { 83 | spinners, 84 | end: () => { 85 | this._dump('activitySetEnd', { id }); 86 | }, 87 | }; 88 | } 89 | activity() { 90 | return this._activity({}); 91 | } 92 | _activity(data) { 93 | if (!this.isTTY || this.noProgress) { 94 | return { 95 | tick() { }, 96 | end() { }, 97 | }; 98 | } 99 | const id = this._activityId++; 100 | this._dump('activityStart', { id, ...data }); 101 | return { 102 | tick: (name) => { 103 | this._dump('activityTick', { id, name }); 104 | }, 105 | end: () => { 106 | this._dump('activityEnd', { id }); 107 | }, 108 | }; 109 | } 110 | progress(total) { 111 | if (this.noProgress) { 112 | return function () { 113 | // noop 114 | }; 115 | } 116 | const id = this._progressId++; 117 | let current = 0; 118 | this._dump('progressStart', { id, total }); 119 | return () => { 120 | current++; 121 | this._dump('progressTick', { id, current }); 122 | if (current === total) { 123 | this._dump('progressFinish', { id }); 124 | } 125 | }; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /checkpoint/dist-src/reporters/lang/index.js: -------------------------------------------------------------------------------- 1 | import en from './en.js'; 2 | export { en }; 3 | -------------------------------------------------------------------------------- /checkpoint/dist-src/reporters/noop-reporter.js: -------------------------------------------------------------------------------- 1 | import BaseReporter from './base-reporter.js'; 2 | export default class NoopReporter extends BaseReporter { 3 | lang(key, ...args) { 4 | return 'do nothing'; 5 | } 6 | verbose(msg) { } 7 | verboseInspect(val) { } 8 | initPeakMemoryCounter() { } 9 | checkPeakMemory() { } 10 | close() { } 11 | getTotalTime() { 12 | return 0; 13 | } 14 | list(key, items, hints) { } 15 | tree(key, obj) { } 16 | step(current, total, message, emoji) { } 17 | error(message) { } 18 | info(message) { } 19 | warn(message) { } 20 | success(message) { } 21 | log(message) { } 22 | command(command) { } 23 | inspect(value) { } 24 | header(pkg) { } 25 | footer(showPeakMemory) { } 26 | table(head, body) { } 27 | activity() { 28 | return { 29 | tick(name) { }, 30 | end() { }, 31 | }; 32 | } 33 | activitySet(total, workers) { 34 | return { 35 | spinners: Array(workers).fill({ 36 | clear() { }, 37 | setPrefix() { }, 38 | tick() { }, 39 | end() { }, 40 | }), 41 | end() { }, 42 | }; 43 | } 44 | progress(total) { 45 | return function () { }; 46 | } 47 | disableProgress() { 48 | this.noProgress = true; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /checkpoint/dist-src/reporters/types.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FredKSchott/pika-pack/db05a6c653f45cd5d2d4801a17b13b7c3b21915d/checkpoint/dist-src/reporters/types.js -------------------------------------------------------------------------------- /checkpoint/dist-src/types.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FredKSchott/pika-pack/db05a6c653f45cd5d2d4801a17b13b7c3b21915d/checkpoint/dist-src/types.js -------------------------------------------------------------------------------- /checkpoint/dist-src/util/babel-plugin-import-rewrite.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import * as nodeFs from 'fs'; 3 | import * as nodePath from 'path'; 4 | import * as url from 'url'; 5 | import chalk from 'chalk'; 6 | import { validateDynamicImportArguments } from './babel-validate-specifier.js'; 7 | const BareIdentifierFormat = /^((?:@[^\/]+\/)?[^\/]+)(\/.*)?$/; 8 | function log(symbol, fileName, errors) { 9 | if (!Array.isArray(errors)) { 10 | errors = [errors]; 11 | } 12 | console.log(`${symbol} `, chalk.bold(fileName)); 13 | for (const error of errors) { 14 | console.log(` ${chalk.dim('≫')} ${error}`); 15 | } 16 | } 17 | export default function transform({ template, types: t }) { 18 | function rewriteImport(specifier, { opts, file }) { 19 | const { deps, addExtensions } = opts; 20 | try { 21 | url.parse(specifier); 22 | } 23 | catch (err) { 24 | return; 25 | } 26 | // URL w/o protocol 27 | if (specifier.substr(0, 2) === '//') { 28 | return; // Leave it alone 29 | } 30 | // Local path 31 | if (['.', '/'].indexOf(specifier.charAt(0)) >= 0) { 32 | if (addExtensions) { 33 | const extname = nodePath.extname(specifier); 34 | if (extname === '.js') { 35 | return; 36 | } 37 | if (extname) { 38 | console.warn('Unexpected file extension:', specifier); 39 | return; 40 | } 41 | const resolvedPath = nodePath.resolve(nodePath.dirname(file.opts.filename), specifier); 42 | try { 43 | const stat = nodeFs.lstatSync(resolvedPath); 44 | if (stat.isDirectory()) { 45 | return specifier + '/index'; 46 | } 47 | } 48 | catch (err) { 49 | // do nothing 50 | } 51 | return specifier + '.js'; 52 | } 53 | return; 54 | } 55 | // A 'bare' identifier 56 | const match = BareIdentifierFormat.exec(specifier); 57 | if (deps && match) { 58 | const packageName = match[1]; 59 | // const file = match[2] || ''; 60 | return deps[packageName]; 61 | } 62 | } 63 | return { 64 | visitor: { 65 | 'ImportDeclaration|ExportNamedDeclaration|ExportAllDeclaration'(path, { opts, file }) { 66 | if (!path.node.source) { 67 | return; 68 | } 69 | const rewrittenSpecifier = rewriteImport(path.node.source.value, { opts, file }); 70 | if (rewrittenSpecifier) { 71 | path.node.source.value = rewrittenSpecifier; 72 | } 73 | }, 74 | Import(path, { opts, file }) { 75 | const errors = validateDynamicImportArguments(path); 76 | if (errors.size > 0) { 77 | return; 78 | } 79 | const [importPath] = path.parent.arguments; 80 | const rewrittenSpecifier = rewriteImport(importPath.value, { opts, file }); 81 | if (rewrittenSpecifier) { 82 | importPath.value = rewrittenSpecifier; 83 | } 84 | }, 85 | }, 86 | }; 87 | } 88 | -------------------------------------------------------------------------------- /checkpoint/dist-src/util/babel-validate-specifier.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import chalk from 'chalk'; 3 | function getLineCol(node) { 4 | const loc = node.loc.start; 5 | return chalk.dim(`[${loc.line}:${loc.column}]`); 6 | } 7 | export function validateDynamicImportArguments(path) { 8 | if (path.parent.arguments.length !== 1) { 9 | return new Set([ 10 | `${getLineCol(path.node)} "\`import()\` only accepts 1 argument, but got ${path.parent.arguments.length}`, 11 | ]); 12 | } 13 | const [argNode] = path.parent.arguments; 14 | if (argNode.type !== 'StringLiteral') { 15 | return new Set([ 16 | `${getLineCol(path.node)} Pika expects strings as \`import()\` arguments. Treating this as an absolute file path.`, 17 | ]); 18 | } 19 | return new Set(); 20 | } 21 | -------------------------------------------------------------------------------- /checkpoint/dist-src/util/blocking-queue.js: -------------------------------------------------------------------------------- 1 | import map from './map.js'; 2 | export default class BlockingQueue { 3 | constructor(alias, maxConcurrency = Infinity) { 4 | this.concurrencyQueue = []; 5 | this.maxConcurrency = maxConcurrency; 6 | this.runningCount = 0; 7 | this.warnedStuck = false; 8 | this.alias = alias; 9 | this.first = true; 10 | this.running = map() || {}; 11 | this.queue = map() || {}; 12 | this.stuckTick = this.stuckTick.bind(this); 13 | } 14 | stillActive() { 15 | if (this.stuckTimer) { 16 | clearTimeout(this.stuckTimer); 17 | } 18 | this.stuckTimer = setTimeout(this.stuckTick, 5000); 19 | // We need to check the existence of unref because of https://github.com/facebook/jest/issues/4559 20 | // $FlowFixMe: Node's setInterval returns a Timeout, not a Number 21 | this.stuckTimer.unref && this.stuckTimer.unref(); 22 | } 23 | stuckTick() { 24 | if (this.runningCount === 1) { 25 | this.warnedStuck = true; 26 | console.log(`The ${JSON.stringify(this.alias)} blocking queue may be stuck. 5 seconds ` + 27 | `without any activity with 1 worker: ${Object.keys(this.running)[0]}`); 28 | } 29 | } 30 | push(key, factory) { 31 | if (this.first) { 32 | this.first = false; 33 | } 34 | else { 35 | this.stillActive(); 36 | } 37 | return new Promise((resolve, reject) => { 38 | // we're already running so push ourselves to the queue 39 | const queue = (this.queue[key] = this.queue[key] || []); 40 | queue.push({ factory, resolve, reject }); 41 | if (!this.running[key]) { 42 | this.shift(key); 43 | } 44 | }); 45 | } 46 | shift(key) { 47 | if (this.running[key]) { 48 | delete this.running[key]; 49 | this.runningCount--; 50 | if (this.stuckTimer) { 51 | clearTimeout(this.stuckTimer); 52 | this.stuckTimer = null; 53 | } 54 | if (this.warnedStuck) { 55 | this.warnedStuck = false; 56 | console.log(`${JSON.stringify(this.alias)} blocking queue finally resolved. Nothing to worry about.`); 57 | } 58 | } 59 | const queue = this.queue[key]; 60 | if (!queue) { 61 | return; 62 | } 63 | const { resolve, reject, factory } = queue.shift(); 64 | if (!queue.length) { 65 | delete this.queue[key]; 66 | } 67 | const next = () => { 68 | this.shift(key); 69 | this.shiftConcurrencyQueue(); 70 | }; 71 | const run = () => { 72 | this.running[key] = true; 73 | this.runningCount++; 74 | factory() 75 | .then(function (val) { 76 | resolve(val); 77 | next(); 78 | return null; 79 | }) 80 | .catch(function (err) { 81 | reject(err); 82 | next(); 83 | }); 84 | }; 85 | this.maybePushConcurrencyQueue(run); 86 | } 87 | maybePushConcurrencyQueue(run) { 88 | if (this.runningCount < this.maxConcurrency) { 89 | run(); 90 | } 91 | else { 92 | this.concurrencyQueue.push(run); 93 | } 94 | } 95 | shiftConcurrencyQueue() { 96 | if (this.runningCount < this.maxConcurrency) { 97 | const fn = this.concurrencyQueue.shift(); 98 | if (fn) { 99 | fn(); 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /checkpoint/dist-src/util/child.js: -------------------------------------------------------------------------------- 1 | /* global child_process$spawnOpts */ 2 | import * as constants from '../constants.js'; 3 | import BlockingQueue from './blocking-queue.js'; 4 | import { ProcessSpawnError, ProcessTermError } from '../errors.js'; 5 | import { promisify } from './promise.js'; 6 | import { exec as _exec, spawn as _spawn } from 'child_process'; 7 | export const queue = new BlockingQueue('child', constants.CHILD_CONCURRENCY); 8 | // TODO: this uid check is kinda whack 9 | let uid = 0; 10 | export const exec = promisify(_exec); 11 | const spawnedProcesses = {}; 12 | export function forwardSignalToSpawnedProcesses(signal) { 13 | for (const key of Object.keys(spawnedProcesses)) { 14 | spawnedProcesses[key].kill(signal); 15 | } 16 | } 17 | export function spawn(program, args, opts = {}, onData) { 18 | const key = opts.cwd || String(++uid); 19 | return queue.push(key, () => new Promise((resolve, reject) => { 20 | const proc = _spawn(program, args, opts); 21 | spawnedProcesses[key] = proc; 22 | let processingDone = false; 23 | let processClosed = false; 24 | let err = null; 25 | let stdout = ''; 26 | proc.on('error', (err) => { 27 | if (err.code === 'ENOENT') { 28 | reject(new ProcessSpawnError(`Couldn't find the binary ${program}`, err.code, program)); 29 | } 30 | else { 31 | reject(err); 32 | } 33 | }); 34 | function updateStdout(chunk) { 35 | stdout += chunk; 36 | if (onData) { 37 | onData(chunk); 38 | } 39 | } 40 | function finish() { 41 | delete spawnedProcesses[key]; 42 | if (err) { 43 | reject(err); 44 | } 45 | else { 46 | resolve(stdout.trim()); 47 | } 48 | } 49 | if (typeof opts.process === 'function') { 50 | opts.process(proc, updateStdout, reject, function () { 51 | if (processClosed) { 52 | finish(); 53 | } 54 | else { 55 | processingDone = true; 56 | } 57 | }); 58 | } 59 | else { 60 | if (proc.stderr) { 61 | proc.stderr.on('data', updateStdout); 62 | } 63 | if (proc.stdout) { 64 | proc.stdout.on('data', updateStdout); 65 | } 66 | processingDone = true; 67 | } 68 | proc.on('close', (code, signal) => { 69 | if (signal || code >= 1) { 70 | err = new ProcessTermError([ 71 | 'Command failed.', 72 | signal ? `Exit signal: ${signal}` : `Exit code: ${code}`, 73 | `Command: ${program}`, 74 | `Arguments: ${args.join(' ')}`, 75 | `Directory: ${opts.cwd || process.cwd()}`, 76 | `Output:\n${stdout.trim()}`, 77 | ].join('\n')); 78 | err.EXIT_SIGNAL = signal; 79 | err.EXIT_CODE = code; 80 | } 81 | if (processingDone || err) { 82 | finish(); 83 | } 84 | else { 85 | processClosed = true; 86 | } 87 | }); 88 | })); 89 | } 90 | -------------------------------------------------------------------------------- /checkpoint/dist-src/util/conversion.js: -------------------------------------------------------------------------------- 1 | const FALSY_STRINGS = new Set(['0', 'false']); 2 | export function boolify(val) { 3 | return !FALSY_STRINGS.has(val.toString().toLowerCase()); 4 | } 5 | export function boolifyWithDefault(val, defaultResult) { 6 | return val === '' || val === null || val === undefined ? defaultResult : boolify(val); 7 | } 8 | -------------------------------------------------------------------------------- /checkpoint/dist-src/util/fix-cmd-win-slashes.js: -------------------------------------------------------------------------------- 1 | export function fixCmdWinSlashes(cmd) { 2 | function findQuotes(quoteSymbol) { 3 | const quotes = []; 4 | const addQuote = (_, index) => { 5 | quotes.push({ from: index, to: index + _.length }); 6 | return _; 7 | }; 8 | const regEx = new RegExp(quoteSymbol + '.*' + quoteSymbol); 9 | cmd.replace(regEx, addQuote); 10 | return quotes; 11 | } 12 | const quotes = findQuotes('"').concat(findQuotes("'")); 13 | function isInsideQuotes(index) { 14 | return quotes.reduce((result, quote) => { 15 | return result || (quote.from <= index && index <= quote.to); 16 | }, false); 17 | } 18 | const cmdPrePattern = '((?:^|&&|&|\\|\\||\\|)\\s*)'; 19 | const cmdPattern = '(".*?"|\'.*?\'|\\S*)'; 20 | const regExp = new RegExp(`${cmdPrePattern}${cmdPattern}`, 'g'); 21 | return cmd.replace(regExp, (whole, pre, cmd, index) => { 22 | if ((pre[0] === '&' || pre[0] === '|') && isInsideQuotes(index)) { 23 | return whole; 24 | } 25 | return pre + cmd.replace(/\//g, '\\'); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /checkpoint/dist-src/util/fs-normalized.js: -------------------------------------------------------------------------------- 1 | // 2 | // // This module serves as a wrapper for file operations that are inconsistant across node and OS versions. 3 | // import fs from 'fs'; 4 | // import {promisify} from './promise.js'; 5 | // import {constants} from './fs.js'; 6 | // export type CopyFileAction = { 7 | // src: string, 8 | // dest: string, 9 | // atime: Date, 10 | // mtime: Date, 11 | // mode: number, 12 | // }; 13 | // let disableTimestampCorrection: boolean; // OS dependent. will be detected on first file copy. 14 | // const readFileBuffer = promisify(fs.readFile); 15 | // const close: (fd: number) => Promise = promisify(fs.close); 16 | // const lstat: (path: string) => Promise = promisify(fs.lstat); 17 | // const open: (path: string, flags: string | number, mode: number) => Promise = promisify(fs.open); 18 | // const futimes: (fd: number, atime: number, mtime: number) => Promise = promisify(fs.futimes); 19 | // const write: ( 20 | // fd: number, 21 | // buffer: Buffer, 22 | // offset?: number, 23 | // length?: number, 24 | // position?: number, 25 | // ) => Promise = promisify(fs.write); 26 | // /** 27 | // * Unlinks the destination to force a recreation. This is needed on case-insensitive file systems 28 | // * to force the correct naming when the filename has changed only in character-casing. (Jest -> jest). 29 | // */ 30 | // export const copyFile = async function(data: CopyFileAction, cleanup: () => any): Promise { 31 | // // $FlowFixMe: Flow doesn't currently support COPYFILE_FICLONE 32 | // const ficloneFlag = (constants as any).COPYFILE_FICLONE || 0; 33 | // try { 34 | // await unlink(data.dest); 35 | // await copyFilePoly(data.src, data.dest, ficloneFlag, data); 36 | // } finally { 37 | // if (cleanup) { 38 | // cleanup(); 39 | // } 40 | // } 41 | // }; 42 | // // Node 8.5.0 introduced `fs.copyFile` which is much faster, so use that when available. 43 | // // Otherwise we fall back to reading and writing files as buffers. 44 | // const copyFilePoly: (src: string, dest: string, flags: number, data: CopyFileAction) => Promise = ( 45 | // src, 46 | // dest, 47 | // flags, 48 | // data, 49 | // ) => { 50 | // if (fs.copyFile) { 51 | // return new Promise((resolve, reject) => 52 | // fs.copyFile(src, dest, flags, err => { 53 | // if (err) { 54 | // reject(err); 55 | // } else { 56 | // fixTimes(undefined, dest, data).then(() => resolve()).catch(ex => reject(ex)); 57 | // } 58 | // }), 59 | // ); 60 | // } else { 61 | // return copyWithBuffer(src, dest, flags, data); 62 | // } 63 | // }; 64 | // const copyWithBuffer: (src: string, dest: string, flags: number, data: CopyFileAction) => Promise = async ( 65 | // src, 66 | // dest, 67 | // flags, 68 | // data, 69 | // ) => { 70 | // // Use open -> write -> futimes -> close sequence to avoid opening the file twice: 71 | // // one with writeFile and one with utimes 72 | // const fd = await open(dest, 'w', data.mode); 73 | // try { 74 | // const buffer = await readFileBuffer(src); 75 | // await write(fd, buffer, 0, buffer.length); 76 | // await fixTimes(fd, dest, data); 77 | // } finally { 78 | // await close(fd); 79 | // } 80 | // }; 81 | // // We want to preserve file timestamps when copying a file, since pika uses them to decide if a file has 82 | // // changed compared to the cache. 83 | // // There are some OS specific cases here: 84 | // // * On linux, fs.copyFile does not preserve timestamps, but does on OSX and Win. 85 | // // * On windows, you must open a file with write permissions to call `fs.futimes`. 86 | // // * On OSX you can open with read permissions and still call `fs.futimes`. 87 | // async function fixTimes(fd: number | undefined, dest: string, data: CopyFileAction): Promise { 88 | // const doOpen = fd === undefined; 89 | // let openfd: number = fd ? fd : -1; 90 | // if (disableTimestampCorrection === undefined) { 91 | // // if timestamps match already, no correction is needed. 92 | // // the need to correct timestamps varies based on OS and node versions. 93 | // const destStat = await lstat(dest); 94 | // disableTimestampCorrection = fileDatesEqual(destStat.mtime, data.mtime); 95 | // } 96 | // if (disableTimestampCorrection) { 97 | // return; 98 | // } 99 | // if (doOpen) { 100 | // try { 101 | // openfd = await open(dest, 'a', data.mode); 102 | // } catch (er) { 103 | // // file is likely read-only 104 | // try { 105 | // openfd = await open(dest, 'r', data.mode); 106 | // } catch (err) { 107 | // // We can't even open this file for reading. 108 | // return; 109 | // } 110 | // } 111 | // } 112 | // try { 113 | // if (openfd) { 114 | // await futimes(openfd, data.atime, data.mtime); 115 | // } 116 | // } catch (er) { 117 | // // If `futimes` throws an exception, we probably have a case of a read-only file on Windows. 118 | // // In this case we can just return. The incorrect timestamp will just cause that file to be recopied 119 | // // on subsequent installs, which will effect pika performance but not break anything. 120 | // } finally { 121 | // if (doOpen && openfd) { 122 | // await close(openfd); 123 | // } 124 | // } 125 | // } 126 | // // Compare file timestamps. 127 | // // Some versions of Node on windows zero the milliseconds when utime is used. 128 | // export const fileDatesEqual = (a: Date, b: Date) => { 129 | // const aTime = a.getTime(); 130 | // const bTime = b.getTime(); 131 | // if (process.platform !== 'win32') { 132 | // return aTime === bTime; 133 | // } 134 | // // See https://github.com/nodejs/node/pull/12607 135 | // // Submillisecond times from stat and utimes are truncated on Windows, 136 | // // causing a file with mtime 8.0079998 and 8.0081144 to become 8.007 and 8.008 137 | // // and making it impossible to update these files to their correct timestamps. 138 | // if (Math.abs(aTime - bTime) <= 1) { 139 | // return true; 140 | // } 141 | // const aTimeSec = Math.floor(aTime / 1000); 142 | // const bTimeSec = Math.floor(bTime / 1000); 143 | // // See https://github.com/nodejs/node/issues/2069 144 | // // Some versions of Node on windows zero the milliseconds when utime is used 145 | // // So if any of the time has a milliseconds part of zero we suspect that the 146 | // // bug is present and compare only seconds. 147 | // if (aTime - aTimeSec * 1000 === 0 || bTime - bTimeSec * 1000 === 0) { 148 | // return aTimeSec === bTimeSec; 149 | // } 150 | // return aTime === bTime; 151 | // }; 152 | -------------------------------------------------------------------------------- /checkpoint/dist-src/util/map.js: -------------------------------------------------------------------------------- 1 | export default function nullify(obj) { 2 | if (Array.isArray(obj)) { 3 | for (const item of obj) { 4 | nullify(item); 5 | } 6 | } 7 | else if ((obj !== null && typeof obj === 'object') || typeof obj === 'function') { 8 | Object.setPrototypeOf(obj, null); 9 | // for..in can only be applied to 'object', not 'function' 10 | if (typeof obj === 'object') { 11 | for (const key in obj) { 12 | nullify(obj[key]); 13 | } 14 | } 15 | } 16 | return obj; 17 | } 18 | -------------------------------------------------------------------------------- /checkpoint/dist-src/util/misc.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | import _camelCase from 'camelcase'; 3 | export function sortAlpha(a, b) { 4 | // sort alphabetically in a deterministic way 5 | const shortLen = Math.min(a.length, b.length); 6 | for (let i = 0; i < shortLen; i++) { 7 | const aChar = a.charCodeAt(i); 8 | const bChar = b.charCodeAt(i); 9 | if (aChar !== bChar) { 10 | return aChar - bChar; 11 | } 12 | } 13 | return a.length - b.length; 14 | } 15 | export function sortOptionsByFlags(a, b) { 16 | const aOpt = a.flags.replace(/-/g, ''); 17 | const bOpt = b.flags.replace(/-/g, ''); 18 | return sortAlpha(aOpt, bOpt); 19 | } 20 | export function entries(obj) { 21 | const entries = []; 22 | if (obj) { 23 | for (const key in obj) { 24 | entries.push([key, obj[key]]); 25 | } 26 | } 27 | return entries; 28 | } 29 | export function removePrefix(pattern, prefix) { 30 | if (pattern.startsWith(prefix)) { 31 | pattern = pattern.slice(prefix.length); 32 | } 33 | return pattern; 34 | } 35 | export function removeSuffix(pattern, suffix) { 36 | if (pattern.endsWith(suffix)) { 37 | return pattern.slice(0, -suffix.length); 38 | } 39 | return pattern; 40 | } 41 | export function addSuffix(pattern, suffix) { 42 | if (!pattern.endsWith(suffix)) { 43 | return pattern + suffix; 44 | } 45 | return pattern; 46 | } 47 | export function hyphenate(str) { 48 | return str.replace(/[A-Z]/g, match => { 49 | return '-' + match.charAt(0).toLowerCase(); 50 | }); 51 | } 52 | export function camelCase(str) { 53 | if (/[A-Z]/.test(str)) { 54 | return null; 55 | } 56 | else { 57 | return _camelCase(str); 58 | } 59 | } 60 | export function compareSortedArrays(array1, array2) { 61 | if (array1.length !== array2.length) { 62 | return false; 63 | } 64 | for (let i = 0, len = array1.length; i < len; i++) { 65 | if (array1[i] !== array2[i]) { 66 | return false; 67 | } 68 | } 69 | return true; 70 | } 71 | export function sleep(ms) { 72 | return new Promise(resolve => { 73 | setTimeout(resolve, ms); 74 | }); 75 | } 76 | -------------------------------------------------------------------------------- /checkpoint/dist-src/util/normalize-manifest/for-publish.js: -------------------------------------------------------------------------------- 1 | export async function generatePublishManifest(manifest, config, _dists) { 2 | const { name, version, description, keywords, homepage, bugs, bin, license, authors, contributors, man, sideEffects, repository, dependencies, peerDependencies, devDependencies, bundledDependencies, optionalDependencies, engines, enginesStrict, private: priv, publishConfig, } = manifest; 3 | const newManifest = { 4 | name, 5 | description, 6 | version, 7 | license, 8 | bin, 9 | files: ['dist-*/', 'bin/'], 10 | pika: true, 11 | sideEffects: sideEffects || false, 12 | keywords, 13 | homepage, 14 | bugs, 15 | authors, 16 | contributors, 17 | man, 18 | repository, 19 | dependencies: dependencies || {}, 20 | peerDependencies, 21 | devDependencies, 22 | bundledDependencies, 23 | optionalDependencies, 24 | engines, 25 | enginesStrict, 26 | private: priv, 27 | publishConfig, 28 | }; 29 | const dists = _dists || (await config.getDistributions()); 30 | for (const [runner, options] of dists) { 31 | if (runner.manifest) { 32 | await runner.manifest(newManifest, { 33 | cwd: config.cwd, 34 | isFull: true, 35 | manifest, 36 | options, 37 | }); 38 | } 39 | } 40 | newManifest.pika = true; 41 | return newManifest; 42 | } 43 | export function generatePrettyManifest(manifest) { 44 | return JSON.stringify({ 45 | ...manifest, 46 | dependencies: Object.keys(manifest.dependencies).length === 0 ? {} : '{ ... }', 47 | }, null, 2); 48 | } 49 | -------------------------------------------------------------------------------- /checkpoint/dist-src/util/normalize-manifest/index.js: -------------------------------------------------------------------------------- 1 | import validate from './validate.js'; 2 | import fix from './fix.js'; 3 | import * as path from 'path'; 4 | export default (async function (info, moduleLoc, config, isRoot) { 5 | // Append dependencies 6 | // if (depInfo) { 7 | // info.dependencies = depInfo.main; 8 | // info.devDependencies = depInfo.dev; 9 | // } 10 | // create human readable name 11 | const { name, version } = info; 12 | let human; 13 | if (typeof name === 'string') { 14 | human = name; 15 | } 16 | if (human && typeof version === 'string' && version) { 17 | human += `@${version}`; 18 | } 19 | if (isRoot && info._loc) { 20 | human = path.relative(config.cwd, info._loc); 21 | } 22 | function warn(msg) { 23 | if (human) { 24 | msg = `${human}: ${msg}`; 25 | } 26 | config.reporter.warn(msg); 27 | } 28 | await fix(info, moduleLoc, config.reporter, warn); 29 | try { 30 | validate(info, isRoot, config.reporter, warn); 31 | } 32 | catch (err) { 33 | if (human) { 34 | err.message = `${human}: ${err.message}`; 35 | } 36 | throw err; 37 | } 38 | return info; 39 | }); 40 | -------------------------------------------------------------------------------- /checkpoint/dist-src/util/normalize-manifest/infer-license.js: -------------------------------------------------------------------------------- 1 | import LICENSES from './licenses.js'; 2 | function clean(str) { 3 | return str.replace(/[^A-Za-z\s]/g, ' ').replace(/[\s]+/g, ' ').trim().toLowerCase(); 4 | } 5 | const REGEXES = { 6 | Apache: [/Apache License\b/], 7 | BSD: [/BSD\b/], 8 | ISC: [/The ISC License/, /ISC\b/], 9 | MIT: [/MIT\b/], 10 | Unlicense: [/http:\/\/unlicense.org\//], 11 | WTFPL: [/DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE/, /WTFPL\b/], 12 | }; 13 | export default function inferLicense(license) { 14 | // check if we have any explicit licenses 15 | const cleanLicense = clean(license); 16 | for (const licenseName in LICENSES) { 17 | const testLicense = LICENSES[licenseName]; 18 | if (cleanLicense.search(testLicense) >= 0) { 19 | return licenseName; 20 | } 21 | } 22 | // infer based on some keywords 23 | for (const licenseName in REGEXES) { 24 | for (const regex of REGEXES[licenseName]) { 25 | if (license.search(regex) >= 0) { 26 | return `${licenseName}*`; 27 | } 28 | } 29 | } 30 | return null; 31 | } 32 | -------------------------------------------------------------------------------- /checkpoint/dist-src/util/normalize-manifest/typos.js: -------------------------------------------------------------------------------- 1 | export default { 2 | autohr: 'author', 3 | autor: 'author', 4 | contributers: 'contributors', 5 | depdenencies: 'dependencies', 6 | dependancies: 'dependencies', 7 | dependecies: 'dependencies', 8 | depends: 'dependencies', 9 | 'dev-dependencies': 'devDependencies', 10 | devDependences: 'devDependencies', 11 | devDepenencies: 'devDependencies', 12 | devEependencies: 'devDependencies', 13 | devdependencies: 'devDependencies', 14 | hampage: 'homepage', 15 | hompage: 'homepage', 16 | prefereGlobal: 'preferGlobal', 17 | publicationConfig: 'publishConfig', 18 | repo: 'repository', 19 | repostitory: 'repository', 20 | script: 'scripts', 21 | }; 22 | -------------------------------------------------------------------------------- /checkpoint/dist-src/util/normalize-manifest/util.js: -------------------------------------------------------------------------------- 1 | import validateLicense from 'validate-npm-package-license'; 2 | export function isValidLicense(license) { 3 | return !!license && validateLicense(license).validForNewPackages; 4 | } 5 | export function stringifyPerson(person) { 6 | if (!person || typeof person !== 'object') { 7 | return person; 8 | } 9 | const parts = []; 10 | if (person.name) { 11 | parts.push(person.name); 12 | } 13 | const email = person.email || person.mail; 14 | if (typeof email === 'string') { 15 | parts.push(`<${email}>`); 16 | } 17 | const url = person.url || person.web; 18 | if (typeof url === 'string') { 19 | parts.push(`(${url})`); 20 | } 21 | return parts.join(' '); 22 | } 23 | export function parsePerson(person) { 24 | if (typeof person !== 'string') { 25 | return person; 26 | } 27 | // format: name (url) 28 | const obj = {}; 29 | let name = person.match(/^([^\(<]+)/); 30 | if (name && name[0].trim()) { 31 | obj.name = name[0].trim(); 32 | } 33 | const email = person.match(/<([^>]+)>/); 34 | if (email) { 35 | obj.email = email[1]; 36 | } 37 | const url = person.match(/\(([^\)]+)\)/); 38 | if (url) { 39 | obj.url = url[1]; 40 | } 41 | return obj; 42 | } 43 | export function normalizePerson(person) { 44 | return parsePerson(stringifyPerson(person)); 45 | } 46 | export function extractDescription(readme) { 47 | if (typeof readme !== 'string' || readme === '') { 48 | return undefined; 49 | } 50 | // split into lines 51 | const lines = readme.trim().split('\n').map((line) => line.trim()); 52 | // find the start of the first paragraph, ignore headings 53 | let start = 0; 54 | for (; start < lines.length; start++) { 55 | const line = lines[start]; 56 | if (line && line.match(/^(#|$)/)) { 57 | // line isn't empty and isn't a heading so this is the start of a paragraph 58 | start++; 59 | break; 60 | } 61 | } 62 | // skip newlines from the header to the first line 63 | while (start < lines.length && !lines[start]) { 64 | start++; 65 | } 66 | // continue to the first non empty line 67 | let end = start; 68 | while (end < lines.length && lines[end]) { 69 | end++; 70 | } 71 | return lines.slice(start, end).join(' '); 72 | } 73 | -------------------------------------------------------------------------------- /checkpoint/dist-src/util/normalize-manifest/validate.js: -------------------------------------------------------------------------------- 1 | import { MessageError } from '@pika/types'; 2 | import typos from './typos.js'; 3 | import isBuiltinModule from 'is-builtin-module'; 4 | const strings = ['name', 'version']; 5 | const dependencyKeys = [ 6 | // npm registry will include optionalDependencies in dependencies and we'll want to dedupe them from the 7 | // other fields first 8 | 'optionalDependencies', 9 | // it's seemingly common to include a dependency in dependencies and devDependencies of the same name but 10 | // different ranges, this can cause a lot of issues with our determinism and the behaviour of npm is 11 | // currently unspecified. 12 | 'dependencies', 13 | 'devDependencies', 14 | ]; 15 | function isValidName(name) { 16 | return !name.match(/[\/@\s\+%:]/) && encodeURIComponent(name) === name; 17 | } 18 | function isValidScopedName(name) { 19 | if (name[0] !== '@') { 20 | return false; 21 | } 22 | const parts = name.slice(1).split('/'); 23 | return parts.length === 2 && isValidName(parts[0]) && isValidName(parts[1]); 24 | } 25 | export function isValidPackageName(name) { 26 | return isValidName(name) || isValidScopedName(name); 27 | } 28 | export default function (info, isRoot, reporter, warn) { 29 | if (isRoot) { 30 | for (const key in typos) { 31 | if (key in info) { 32 | warn(reporter.lang('manifestPotentialTypo', key, typos[key])); 33 | } 34 | } 35 | } 36 | // validate name 37 | const { name } = info; 38 | if (typeof name === 'string') { 39 | if (isRoot && isBuiltinModule(name)) { 40 | warn(reporter.lang('manifestBuiltinModule', name)); 41 | } 42 | // cannot start with a dot 43 | if (name[0] === '.') { 44 | throw new MessageError(reporter.lang('manifestNameDot')); 45 | } 46 | // cannot contain the following characters 47 | if (!isValidPackageName(name)) { 48 | throw new MessageError(reporter.lang('manifestNameIllegalChars')); 49 | } 50 | // cannot equal node_modules or favicon.ico 51 | const lower = name.toLowerCase(); 52 | if (lower === 'node_modules' || lower === 'favicon.ico') { 53 | throw new MessageError(reporter.lang('manifestNameBlacklisted')); 54 | } 55 | } 56 | // Only care if you are trying to publish to npm. 57 | // // validate license 58 | // if (isRoot && !info.private) { 59 | // if (typeof info.license === 'string') { 60 | // const license = info.license.replace(/\*$/g, ''); 61 | // if (!isValidLicense(license)) { 62 | // warn(reporter.lang('manifestLicenseInvalid')); 63 | // } 64 | // } else { 65 | // warn(reporter.lang('manifestLicenseNone')); 66 | // } 67 | // } 68 | // validate strings 69 | for (const key of strings) { 70 | const val = info[key]; 71 | if (val && typeof val !== 'string') { 72 | throw new MessageError(reporter.lang('manifestStringExpected', key)); 73 | } 74 | } 75 | cleanDependencies(info, isRoot, reporter, warn); 76 | } 77 | export function cleanDependencies(info, isRoot, reporter, warn) { 78 | // get dependency objects 79 | const depTypes = []; 80 | for (const type of dependencyKeys) { 81 | const deps = info[type]; 82 | if (!deps || typeof deps !== 'object') { 83 | continue; 84 | } 85 | depTypes.push([type, deps]); 86 | } 87 | // aggregate all non-trivial deps (not '' or '*') 88 | const nonTrivialDeps = new Map(); 89 | for (const [type, deps] of depTypes) { 90 | for (const name of Object.keys(deps)) { 91 | const version = deps[name]; 92 | if (!nonTrivialDeps.has(name) && version && version !== '*') { 93 | nonTrivialDeps.set(name, { type, version }); 94 | } 95 | } 96 | } 97 | // overwrite first dep of package with non-trivial version, remove the rest 98 | const setDeps = new Set(); 99 | for (const [type, deps] of depTypes) { 100 | for (const name of Object.keys(deps)) { 101 | let version = deps[name]; 102 | const dep = nonTrivialDeps.get(name); 103 | if (dep) { 104 | if (version && version !== '*' && version !== dep.version && isRoot) { 105 | // only throw a warning when at the root 106 | warn(reporter.lang('manifestDependencyCollision', dep.type, name, dep.version, type, version)); 107 | } 108 | version = dep.version; 109 | } 110 | if (setDeps.has(name)) { 111 | delete deps[name]; 112 | } 113 | else { 114 | deps[name] = version; 115 | setDeps.add(name); 116 | } 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /checkpoint/dist-src/util/promise.js: -------------------------------------------------------------------------------- 1 | export function wait(delay) { 2 | return new Promise(resolve => { 3 | setTimeout(resolve, delay); 4 | }); 5 | } 6 | export function promisify(fn, firstData) { 7 | return function (...args) { 8 | return new Promise(function (resolve, reject) { 9 | args.push(function (err, ...result) { 10 | let res = result; 11 | if (result.length <= 1) { 12 | res = result[0]; 13 | } 14 | if (firstData) { 15 | res = err; 16 | err = null; 17 | } 18 | if (err) { 19 | reject(err); 20 | } 21 | else { 22 | resolve(res); 23 | } 24 | }); 25 | fn.apply(null, args); 26 | }); 27 | }; 28 | } 29 | export function queue(arr, promiseProducer, concurrency = Infinity) { 30 | concurrency = Math.min(concurrency, arr.length); 31 | // clone 32 | arr = arr.slice(); 33 | const results = []; 34 | let total = arr.length; 35 | if (!total) { 36 | return Promise.resolve(results); 37 | } 38 | return new Promise((resolve, reject) => { 39 | for (let i = 0; i < concurrency; i++) { 40 | next(); 41 | } 42 | function next() { 43 | const item = arr.shift(); 44 | const promise = promiseProducer(item); 45 | promise.then(function (result) { 46 | results.push(result); 47 | total--; 48 | if (total === 0) { 49 | resolve(results); 50 | } 51 | else { 52 | if (arr.length) { 53 | next(); 54 | } 55 | } 56 | }, reject); 57 | } 58 | }); 59 | } 60 | -------------------------------------------------------------------------------- /checkpoint/dist-src/util/signal-handler.js: -------------------------------------------------------------------------------- 1 | import { forwardSignalToSpawnedProcesses } from './child.js'; 2 | function forwardSignalAndExit(signal) { 3 | forwardSignalToSpawnedProcesses(signal); 4 | // We want to exit immediately here since `SIGTERM` means that 5 | // If we lose stdout messages due to abrupt exit, shoot the messenger? 6 | process.exit(1); // eslint-disable-line no-process-exit 7 | } 8 | export default function handleSignals() { 9 | process.on('SIGTERM', () => { 10 | forwardSignalAndExit('SIGTERM'); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /checkpoint/dist-types/commands/build.d.ts: -------------------------------------------------------------------------------- 1 | import Config, { BuildFlags } from '../config.js'; 2 | import { Reporter } from '../reporters/index.js'; 3 | export declare function hasWrapper(): boolean; 4 | export declare const examples: any; 5 | export declare class Build { 6 | constructor(flags: BuildFlags, config: Config, reporter: Reporter); 7 | out: string; 8 | flags: BuildFlags; 9 | config: Config; 10 | reporter: Reporter; 11 | totalNum: number; 12 | cleanup(): Promise; 13 | init(isFull?: boolean): Promise; 14 | } 15 | export declare function run(config: Config, reporter: Reporter, flags: BuildFlags, args: Array): Promise; 16 | -------------------------------------------------------------------------------- /checkpoint/dist-types/config.d.ts: -------------------------------------------------------------------------------- 1 | import { Manifest } from './types.js'; 2 | import BaseReporter from './reporters/base-reporter.js'; 3 | export interface BuildFlags { 4 | publish?: boolean; 5 | out?: string; 6 | silent?: boolean; 7 | force?: boolean; 8 | } 9 | export interface GlobalFlags extends BuildFlags { 10 | cwd?: string; 11 | pipeline?: string; 12 | verbose?: boolean; 13 | json?: boolean; 14 | } 15 | export default class Config { 16 | cwd: string; 17 | reporter: BaseReporter; 18 | _manifest: any; 19 | manifest: Manifest; 20 | flags: GlobalFlags; 21 | constructor(reporter: BaseReporter, cwd: string, flags: GlobalFlags); 22 | loadPackageManifest(): Promise; 23 | readJson(loc: string, factory?: (filename: string) => Promise): Promise; 24 | getDistributions(): Promise<[any, any][]>; 25 | } 26 | -------------------------------------------------------------------------------- /checkpoint/dist-types/constants.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export declare const DEPENDENCY_TYPES: string[]; 3 | export declare const RESOLUTIONS = "resolutions"; 4 | export declare const MANIFEST_FIELDS: string[]; 5 | export declare const SUPPORTED_NODE_VERSIONS = ">=8.5.0"; 6 | export declare const CHILD_CONCURRENCY = 5; 7 | export declare const NODE_MODULES_FOLDER = "node_modules"; 8 | export declare const NODE_PACKAGE_JSON = "package.json"; 9 | export declare const DEFAULT_INDENT = " "; 10 | export declare const ENV_PATH_KEY: string; 11 | export declare function getPathKey(platform: string, env: NodeJS.ProcessEnv): string; 12 | -------------------------------------------------------------------------------- /checkpoint/dist-types/errors.d.ts: -------------------------------------------------------------------------------- 1 | import { MessageError } from '@pika/types'; 2 | export declare class ProcessSpawnError extends MessageError { 3 | constructor(msg: string, code?: string, process?: string); 4 | code?: string; 5 | process?: string; 6 | } 7 | export declare class SecurityError extends MessageError { 8 | } 9 | export declare class ProcessTermError extends MessageError { 10 | EXIT_CODE?: number; 11 | EXIT_SIGNAL?: string; 12 | } 13 | export declare class ResponseError extends Error { 14 | constructor(msg: string, responseCode: number); 15 | responseCode: number; 16 | } 17 | export declare class OneTimePasswordError extends Error { 18 | } 19 | -------------------------------------------------------------------------------- /checkpoint/dist-types/index.d.ts: -------------------------------------------------------------------------------- 1 | export declare function cli(args: string[]): Promise; 2 | -------------------------------------------------------------------------------- /checkpoint/dist-types/reporters/base-reporter.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { ReporterSpinnerSet, Trees, Stdout, Stdin, Package, ReporterSpinner } from './types'; 3 | import { LanguageKeys } from './lang/en.js'; 4 | import { Formatter } from './format.js'; 5 | import * as languages from './lang/index.js'; 6 | declare type Language = keyof typeof languages; 7 | export declare type ReporterOptions = { 8 | verbose?: boolean; 9 | language?: Language; 10 | stdout?: Stdout; 11 | stderr?: Stdout; 12 | stdin?: Stdin; 13 | emoji?: boolean; 14 | noProgress?: boolean; 15 | silent?: boolean; 16 | isSilent?: boolean; 17 | nonInteractive?: boolean; 18 | }; 19 | export declare function stringifyLangArgs(args: Array): Array; 20 | export default class BaseReporter { 21 | constructor(opts?: ReporterOptions); 22 | formatter: Formatter; 23 | language: Language; 24 | stdout: Stdout; 25 | stderr: Stdout; 26 | stdin: Stdin; 27 | isTTY: boolean; 28 | emoji: boolean; 29 | noProgress: boolean; 30 | isVerbose: boolean; 31 | isSilent: boolean; 32 | nonInteractive: boolean; 33 | format: Formatter; 34 | peakMemoryInterval?: NodeJS.Timer; 35 | peakMemory: number; 36 | startTime: number; 37 | lang(key: LanguageKeys, ...args: Array): string; 38 | /** 39 | * `stringifyLangArgs` run `JSON.stringify` on strings too causing 40 | * them to appear quoted. This marks them as "raw" and prevents 41 | * the quoting and escaping 42 | */ 43 | rawText(str: string): { 44 | inspect(): string; 45 | }; 46 | verbose(msg: string): void; 47 | verboseInspect(val: any): void; 48 | _verbose(msg: string): void; 49 | _verboseInspect(val: any): void; 50 | _getStandardInput(): Stdin; 51 | initPeakMemoryCounter(): void; 52 | checkPeakMemory(): void; 53 | close(): void; 54 | getTotalTime(): number; 55 | list(key: string, items: Array, hints?: Object): void; 56 | tree(key: string, obj: Trees, { force }?: { 57 | force?: boolean; 58 | }): void; 59 | step(current: number, total: number, message: string, emoji?: string): void; 60 | error(message: string): void; 61 | info(message: string): void; 62 | warn(message: string): void; 63 | success(message: string): void; 64 | log(message: string, { force }?: { 65 | force?: boolean; 66 | }): void; 67 | command(command: string): void; 68 | inspect(value: any): void; 69 | header(pkg: Package): void; 70 | footer(showPeakMemory: boolean): void; 71 | table(head: Array, body: Array>): void; 72 | activity(): ReporterSpinner; 73 | activitySet(total: number, workers: number): ReporterSpinnerSet; 74 | progress(total: number): () => void; 75 | disableProgress(): void; 76 | } 77 | export {}; 78 | -------------------------------------------------------------------------------- /checkpoint/dist-types/reporters/console/console-reporter.d.ts: -------------------------------------------------------------------------------- 1 | import BaseReporter, { ReporterOptions } from '../base-reporter.js'; 2 | import { FormatKeys } from '../format.js'; 3 | import { Package, ReporterSpinner, ReporterSpinnerSet, Trees } from '../types.js'; 4 | import Progress from './progress-bar.js'; 5 | import Spinner from './spinner-progress.js'; 6 | declare type Row = Array; 7 | export default class ConsoleReporter extends BaseReporter { 8 | _lastCategorySize: number; 9 | _progressBar?: Progress; 10 | _spinners: Set; 11 | constructor(opts: ReporterOptions); 12 | _prependEmoji(msg: string, emoji?: string): string; 13 | _logCategory(category: string, color: FormatKeys, msg: string): void; 14 | _verbose(msg: string): void; 15 | _verboseInspect(obj: any): void; 16 | close(): void; 17 | table(head: Array, body: Array): void; 18 | step(current: number, total: number, msg: string, emoji?: string): void; 19 | inspect(value: any): void; 20 | list(key: string, items: Array, hints?: Object): void; 21 | header(pkg: Package): void; 22 | footer(showPeakMemory?: boolean): void; 23 | log(msg: string, { force }?: { 24 | force?: boolean; 25 | }): void; 26 | _log(msg: string, { force }?: { 27 | force?: boolean; 28 | }): void; 29 | success(msg: string): void; 30 | error(msg: string): void; 31 | info(msg: string): void; 32 | command(command: string): void; 33 | warn(msg: string): void; 34 | tree(key: string, trees: Trees, { force }?: { 35 | force?: boolean; 36 | }): void; 37 | activitySet(total: number, workers: number): ReporterSpinnerSet; 38 | activity(): ReporterSpinner; 39 | progress(count: number): () => void; 40 | stopProgress(): void; 41 | } 42 | export {}; 43 | -------------------------------------------------------------------------------- /checkpoint/dist-types/reporters/console/helpers/tree-helper.d.ts: -------------------------------------------------------------------------------- 1 | import { Trees } from '../../types.js'; 2 | export declare type FormattedOutput = { 3 | prefix: string; 4 | hint: any; 5 | color: string; 6 | name: string; 7 | formatter: any; 8 | }; 9 | export declare function sortTrees(trees: Trees): Trees; 10 | export declare function recurseTree(tree: Trees, prefix: string, recurseFunc: Function): void; 11 | export declare function getFormattedOutput(fmt: FormattedOutput): string; 12 | -------------------------------------------------------------------------------- /checkpoint/dist-types/reporters/console/progress-bar.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { Stdout } from '../types.js'; 3 | export default class ProgressBar { 4 | constructor(total: number, stdout?: Stdout, callback?: (progressBar: ProgressBar) => void); 5 | stdout: Stdout; 6 | curr: number; 7 | total: number; 8 | width: number; 9 | chars: [string, string]; 10 | delay: number; 11 | id?: NodeJS.Timeout; 12 | _callback?: (progressBar: ProgressBar) => void; 13 | static bars: [string, string][]; 14 | tick(): void; 15 | cancelTick(): void; 16 | stop(): void; 17 | render(): void; 18 | } 19 | -------------------------------------------------------------------------------- /checkpoint/dist-types/reporters/console/spinner-progress.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { Stdout } from '../types.js'; 3 | export default class Spinner { 4 | constructor(stdout?: Stdout, lineNumber?: number); 5 | stdout: Stdout; 6 | prefix: string; 7 | current: number; 8 | lineNumber: number; 9 | delay: number; 10 | chars: Array; 11 | text: string; 12 | id?: NodeJS.Timeout; 13 | static spinners: Array; 14 | setPrefix(prefix: string): void; 15 | setText(text: string): void; 16 | start(): void; 17 | render(): void; 18 | stop(): void; 19 | } 20 | -------------------------------------------------------------------------------- /checkpoint/dist-types/reporters/console/util.d.ts: -------------------------------------------------------------------------------- 1 | import { Stdout } from '../types.js'; 2 | export declare function clearLine(stdout: Stdout): void; 3 | export declare function toStartOfLine(stdout: Stdout): void; 4 | export declare function writeOnNthLine(stdout: Stdout, n: number, msg: string): void; 5 | export declare function clearNthLine(stdout: Stdout, n: number): void; 6 | -------------------------------------------------------------------------------- /checkpoint/dist-types/reporters/format.d.ts: -------------------------------------------------------------------------------- 1 | declare function formatFunction(...strs: Array): string; 2 | export declare const defaultFormatter: { 3 | bold: typeof formatFunction; 4 | dim: typeof formatFunction; 5 | italic: typeof formatFunction; 6 | underline: typeof formatFunction; 7 | inverse: typeof formatFunction; 8 | strikethrough: typeof formatFunction; 9 | black: typeof formatFunction; 10 | red: typeof formatFunction; 11 | green: typeof formatFunction; 12 | yellow: typeof formatFunction; 13 | blue: typeof formatFunction; 14 | magenta: typeof formatFunction; 15 | cyan: typeof formatFunction; 16 | white: typeof formatFunction; 17 | gray: typeof formatFunction; 18 | grey: typeof formatFunction; 19 | stripColor: typeof formatFunction; 20 | }; 21 | declare type FormatFunction = (...strs: Array) => string; 22 | export declare type FormatKeys = keyof typeof defaultFormatter; 23 | export declare type Formatter = { 24 | bold: FormatFunction; 25 | dim: FormatFunction; 26 | italic: FormatFunction; 27 | underline: FormatFunction; 28 | inverse: FormatFunction; 29 | strikethrough: FormatFunction; 30 | black: FormatFunction; 31 | red: FormatFunction; 32 | green: FormatFunction; 33 | yellow: FormatFunction; 34 | blue: FormatFunction; 35 | magenta: FormatFunction; 36 | cyan: FormatFunction; 37 | white: FormatFunction; 38 | gray: FormatFunction; 39 | grey: FormatFunction; 40 | stripColor: FormatFunction; 41 | }; 42 | export {}; 43 | -------------------------------------------------------------------------------- /checkpoint/dist-types/reporters/index.d.ts: -------------------------------------------------------------------------------- 1 | export { default as ConsoleReporter } from './console/console-reporter'; 2 | export { default as JSONReporter } from './json-reporter'; 3 | export { default as NoopReporter } from './noop-reporter'; 4 | export { default as Reporter } from './base-reporter'; 5 | -------------------------------------------------------------------------------- /checkpoint/dist-types/reporters/json-reporter.d.ts: -------------------------------------------------------------------------------- 1 | import { ReporterSpinnerSet, Trees, ReporterSpinner } from './types.js'; 2 | import BaseReporter from './base-reporter.js'; 3 | export default class JSONReporter extends BaseReporter { 4 | constructor(opts?: Object); 5 | _activityId: number; 6 | _progressId: number; 7 | _dump(type: string, data: any, error?: boolean): void; 8 | _verbose(msg: string): void; 9 | list(type: string, items: Array, hints?: Object): void; 10 | tree(type: string, trees: Trees): void; 11 | step(current: number, total: number, message: string): void; 12 | inspect(value: any): void; 13 | footer(showPeakMemory: boolean): void; 14 | log(msg: string): void; 15 | command(msg: string): void; 16 | table(head: Array, body: Array>): void; 17 | success(msg: string): void; 18 | error(msg: string): void; 19 | warn(msg: string): void; 20 | info(msg: string): void; 21 | activitySet(total: number, workers: number): ReporterSpinnerSet; 22 | activity(): ReporterSpinner; 23 | _activity(data: Object): ReporterSpinner; 24 | progress(total: number): () => void; 25 | } 26 | -------------------------------------------------------------------------------- /checkpoint/dist-types/reporters/lang/index.d.ts: -------------------------------------------------------------------------------- 1 | import en from './en.js'; 2 | export { en }; 3 | -------------------------------------------------------------------------------- /checkpoint/dist-types/reporters/noop-reporter.d.ts: -------------------------------------------------------------------------------- 1 | import BaseReporter from './base-reporter.js'; 2 | import { LanguageKeys } from './lang/en.js'; 3 | import { Package, ReporterSpinner, ReporterSpinnerSet, Trees } from './types.js'; 4 | export default class NoopReporter extends BaseReporter { 5 | lang(key: LanguageKeys, ...args: Array): string; 6 | verbose(msg: string): void; 7 | verboseInspect(val: any): void; 8 | initPeakMemoryCounter(): void; 9 | checkPeakMemory(): void; 10 | close(): void; 11 | getTotalTime(): number; 12 | list(key: string, items: Array, hints?: Object): void; 13 | tree(key: string, obj: Trees): void; 14 | step(current: number, total: number, message: string, emoji?: string): void; 15 | error(message: string): void; 16 | info(message: string): void; 17 | warn(message: string): void; 18 | success(message: string): void; 19 | log(message: string): void; 20 | command(command: string): void; 21 | inspect(value: any): void; 22 | header(pkg: Package): void; 23 | footer(showPeakMemory: boolean): void; 24 | table(head: Array, body: Array>): void; 25 | activity(): ReporterSpinner; 26 | activitySet(total: number, workers: number): ReporterSpinnerSet; 27 | progress(total: number): () => void; 28 | disableProgress(): void; 29 | } 30 | -------------------------------------------------------------------------------- /checkpoint/dist-types/reporters/types.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { Writable, Readable } from 'stream'; 3 | import { WriteStream, ReadStream } from 'fs'; 4 | export declare type Stdout = Writable | WriteStream; 5 | export declare type Stdin = Readable | ReadStream; 6 | export declare type Package = { 7 | name: string; 8 | version: string; 9 | }; 10 | export declare type Tree = { 11 | name: string; 12 | children?: Trees; 13 | hint?: string; 14 | hidden?: boolean; 15 | color?: string; 16 | }; 17 | export declare type Trees = Array; 18 | export declare type ReporterSpinner = { 19 | tick: (name: string) => void; 20 | end: () => void; 21 | }; 22 | export declare type ReporterSpinnerSet = { 23 | spinners: Array; 24 | end: () => void; 25 | }; 26 | export declare type ReporterSetSpinner = { 27 | clear: () => void; 28 | setPrefix: (current: number, prefix: string) => void; 29 | tick: (msg: string) => void; 30 | end: () => void; 31 | }; 32 | -------------------------------------------------------------------------------- /checkpoint/dist-types/types.d.ts: -------------------------------------------------------------------------------- 1 | import { Reporter } from './reporters/index.js'; 2 | import Config from './config.js'; 3 | export declare type CLIFunction = (config: Config, reporter: Reporter, flags: Object, args: Array) => CLIFunctionReturn; 4 | declare type _CLIFunctionReturn = boolean; 5 | export declare type CLIFunctionReturn = _CLIFunctionReturn | Promise<_CLIFunctionReturn>; 6 | export declare type PersonObject = { 7 | email?: string; 8 | name?: string; 9 | url?: string; 10 | }; 11 | declare type Dependencies = { 12 | [key: string]: string; 13 | }; 14 | export declare type Manifest = { 15 | name: string; 16 | version: string; 17 | description?: string; 18 | keywords?: string[]; 19 | sideEffects?: boolean; 20 | private?: boolean; 21 | distributions?: any; 22 | author?: { 23 | name?: string; 24 | email?: string; 25 | url?: string; 26 | }; 27 | homepage?: string; 28 | flat?: boolean; 29 | license?: string; 30 | licenseText?: string; 31 | noticeText?: string; 32 | readme?: string; 33 | readmeFilename?: string; 34 | repository?: { 35 | type: 'git'; 36 | url: string; 37 | }; 38 | bugs?: { 39 | url: string; 40 | }; 41 | dist?: { 42 | tarball: string; 43 | shasum: string; 44 | }; 45 | directories?: { 46 | man: string; 47 | bin: string; 48 | }; 49 | man?: Array; 50 | bin?: { 51 | [name: string]: string; 52 | }; 53 | scripts?: { 54 | [name: string]: string; 55 | }; 56 | engines?: { 57 | [engineName: string]: string; 58 | }; 59 | os?: Array; 60 | cpu?: Array; 61 | dependencies?: Dependencies; 62 | devDependencies?: Dependencies; 63 | peerDependencies?: Dependencies; 64 | optionalDependencies?: Dependencies; 65 | bundleDependencies?: Array; 66 | bundledDependencies?: Array; 67 | installConfig?: { 68 | pnp?: boolean; 69 | }; 70 | deprecated?: string; 71 | files?: Array; 72 | main?: string; 73 | fresh?: boolean; 74 | prebuiltVariants?: { 75 | [filename: string]: string; 76 | }; 77 | }; 78 | export declare type Dependency = { 79 | name: string; 80 | current: string; 81 | wanted: string; 82 | latest: string; 83 | url: string; 84 | hint?: string; 85 | range: string; 86 | upgradeTo: string; 87 | workspaceName: string; 88 | workspaceLoc: string; 89 | }; 90 | export {}; 91 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/babel-plugin-import-rewrite.d.ts: -------------------------------------------------------------------------------- 1 | export default function transform({ template, types: t }: { 2 | template: any; 3 | types: any; 4 | }): any; 5 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/babel-validate-specifier.d.ts: -------------------------------------------------------------------------------- 1 | export declare function validateDynamicImportArguments(path: any): Set; 2 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/blocking-queue.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export default class BlockingQueue { 3 | constructor(alias: string, maxConcurrency?: number); 4 | concurrencyQueue: Array<() => void>; 5 | warnedStuck: boolean; 6 | maxConcurrency: number; 7 | runningCount: number; 8 | stuckTimer?: NodeJS.Timeout; 9 | alias: string; 10 | first: boolean; 11 | queue: { 12 | [key: string]: Array<{ 13 | factory: () => Promise; 14 | resolve: (val: any) => void; 15 | reject: Function; 16 | }>; 17 | }; 18 | running: { 19 | [key: string]: boolean; 20 | }; 21 | stillActive(): void; 22 | stuckTick(): void; 23 | push(key: string, factory: () => Promise): Promise; 24 | shift(key: string): void; 25 | maybePushConcurrencyQueue(run: () => void): void; 26 | shiftConcurrencyQueue(): void; 27 | } 28 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/child.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import BlockingQueue from './blocking-queue.js'; 3 | import { ChildProcess, SpawnOptions } from 'child_process'; 4 | export declare const queue: BlockingQueue; 5 | export declare const exec: (...args: any[]) => Promise; 6 | export declare function forwardSignalToSpawnedProcesses(signal: string): void; 7 | declare type ProcessFn = (proc: ChildProcess, update: (chunk: string) => void, reject: (err: any) => void, done: () => void) => void; 8 | export declare function spawn(program: string, args: Array, opts?: SpawnOptions & { 9 | detached?: boolean; 10 | process?: ProcessFn; 11 | }, onData?: (chunk: Buffer | string) => void): Promise; 12 | export {}; 13 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/conversion.d.ts: -------------------------------------------------------------------------------- 1 | export declare function boolify(val: string | number | boolean): boolean; 2 | export declare function boolifyWithDefault(val: string | number | boolean, defaultResult: boolean): boolean; 3 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/execute-lifecycle-script.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export declare type LifecycleReturn = Promise<{ 3 | cwd: string; 4 | command: string; 5 | stdout: string; 6 | }>; 7 | export declare function makeEnv(): Promise<{ 8 | [key: string]: string; 9 | }>; 10 | export declare function executeLifecycleScript({ cwd, cmd, args, isInteractive, onProgress, customShell, }: { 11 | cwd: string; 12 | args: string[]; 13 | cmd: string; 14 | isInteractive?: boolean; 15 | onProgress?: (chunk: Buffer | string) => void; 16 | customShell?: string; 17 | }): LifecycleReturn; 18 | export default executeLifecycleScript; 19 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/fix-cmd-win-slashes.d.ts: -------------------------------------------------------------------------------- 1 | export declare function fixCmdWinSlashes(cmd: string): string; 2 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/fs-normalized.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FredKSchott/pika-pack/db05a6c653f45cd5d2d4801a17b13b7c3b21915d/checkpoint/dist-types/util/fs-normalized.d.ts -------------------------------------------------------------------------------- /checkpoint/dist-types/util/fs.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export declare const unlink: (path: string) => Promise; 3 | export declare const glob: (path: string, options?: Object) => Promise>; 4 | export declare const mkdirp: (path: string) => Promise; 5 | import * as fs from 'fs'; 6 | export declare const open: typeof fs.open.__promisify__; 7 | export declare const writeFile: typeof fs.writeFile.__promisify__; 8 | export declare const readlink: typeof fs.readlink.__promisify__; 9 | export declare const realpath: typeof fs.realpath.__promisify__; 10 | export declare const readdir: typeof fs.readdir.__promisify__; 11 | export declare const rename: typeof fs.rename.__promisify__; 12 | export declare const access: typeof fs.access.__promisify__; 13 | export declare const stat: typeof fs.stat.__promisify__; 14 | export declare const exists: typeof fs.exists.__promisify__; 15 | export declare const lstat: typeof fs.lstat.__promisify__; 16 | export declare const chmod: typeof fs.chmod.__promisify__; 17 | export declare const link: (arg1: fs.PathLike, arg2: fs.PathLike) => Promise; 18 | export declare const copyFile: typeof fs.copyFile.__promisify__; 19 | export declare const readFile: (path: string) => Promise; 20 | export declare function readJson(loc: string): Promise; 21 | export declare function readJsonAndFile(loc: string): Promise<{ 22 | object: Object; 23 | content: string; 24 | }>; 25 | export declare type WalkFiles = Array<{ 26 | relative: string; 27 | absolute: string; 28 | basename: string; 29 | mtime: number; 30 | }>; 31 | export declare function walk(dir: string, relativeDir?: string, ignoreBasenames?: Set): Promise; 32 | export declare function writeFilePreservingEol(path: string, data: string): Promise; 33 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/map.d.ts: -------------------------------------------------------------------------------- 1 | export default function nullify(obj?: T): T; 2 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/misc.d.ts: -------------------------------------------------------------------------------- 1 | export declare function sortAlpha(a: string, b: string): number; 2 | export declare function sortOptionsByFlags(a: any, b: any): number; 3 | export declare function entries(obj: { 4 | [key: string]: T; 5 | }): Array<[string, T]>; 6 | export declare function removePrefix(pattern: string, prefix: string): string; 7 | export declare function removeSuffix(pattern: string, suffix: string): string; 8 | export declare function addSuffix(pattern: string, suffix: string): string; 9 | export declare function hyphenate(str: string): string; 10 | export declare function camelCase(str: string): string | null; 11 | export declare function compareSortedArrays(array1: Array, array2: Array): boolean; 12 | export declare function sleep(ms: number): Promise; 13 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/normalize-manifest/fix.d.ts: -------------------------------------------------------------------------------- 1 | import { Reporter } from '../../reporters/index.js'; 2 | declare type Dict = { 3 | [key: string]: T; 4 | }; 5 | declare type WarnFunction = (msg: string) => void; 6 | declare const _default: (info: Dict, moduleLoc: string, reporter: Reporter, warn: WarnFunction) => Promise; 7 | export default _default; 8 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/normalize-manifest/for-publish.d.ts: -------------------------------------------------------------------------------- 1 | import Config from '../../config'; 2 | export declare function generatePublishManifest(manifest: any, config: Config, _dists?: Array<[Function, any]>): Promise; 3 | export declare function generatePrettyManifest(manifest: any): string; 4 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/normalize-manifest/index.d.ts: -------------------------------------------------------------------------------- 1 | import { Manifest } from '../../types.js'; 2 | import Config from '../../config.js'; 3 | declare const _default: (info: any, moduleLoc: string, config: Config, isRoot: boolean) => Promise; 4 | export default _default; 5 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/normalize-manifest/infer-license.d.ts: -------------------------------------------------------------------------------- 1 | export default function inferLicense(license: string): string | null; 2 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/normalize-manifest/licenses.d.ts: -------------------------------------------------------------------------------- 1 | declare const _default: { 2 | 'Apache-2.0': RegExp; 3 | 'BSD-2-Clause': RegExp; 4 | 'BSD-3-Clause': RegExp; 5 | MIT: RegExp; 6 | Unlicense: RegExp; 7 | }; 8 | export default _default; 9 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/normalize-manifest/typos.d.ts: -------------------------------------------------------------------------------- 1 | declare const _default: { 2 | autohr: string; 3 | autor: string; 4 | contributers: string; 5 | depdenencies: string; 6 | dependancies: string; 7 | dependecies: string; 8 | depends: string; 9 | 'dev-dependencies': string; 10 | devDependences: string; 11 | devDepenencies: string; 12 | devEependencies: string; 13 | devdependencies: string; 14 | hampage: string; 15 | hompage: string; 16 | prefereGlobal: string; 17 | publicationConfig: string; 18 | repo: string; 19 | repostitory: string; 20 | script: string; 21 | }; 22 | export default _default; 23 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/normalize-manifest/util.d.ts: -------------------------------------------------------------------------------- 1 | import { PersonObject } from '../../types.js'; 2 | export declare function isValidLicense(license: string): boolean; 3 | export declare function stringifyPerson(person: any): any; 4 | export declare function parsePerson(person: any): PersonObject; 5 | export declare function normalizePerson(person: any): any | PersonObject; 6 | export declare function extractDescription(readme: any): string; 7 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/normalize-manifest/validate.d.ts: -------------------------------------------------------------------------------- 1 | import { Reporter } from '../../reporters/index.js'; 2 | export declare function isValidPackageName(name: string): boolean; 3 | declare type WarnFunction = (msg: string) => void; 4 | export default function (info: any, isRoot: boolean, reporter: Reporter, warn: WarnFunction): void; 5 | export declare function cleanDependencies(info: Object, isRoot: boolean, reporter: Reporter, warn: WarnFunction): void; 6 | export {}; 7 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/promise.d.ts: -------------------------------------------------------------------------------- 1 | export declare function wait(delay: number): Promise; 2 | export declare function promisify(fn: Function, firstData?: boolean): (...args: Array) => Promise; 3 | export declare function queue(arr: Array, promiseProducer: (result: U) => Promise, concurrency?: number): Promise>; 4 | -------------------------------------------------------------------------------- /checkpoint/dist-types/util/signal-handler.d.ts: -------------------------------------------------------------------------------- 1 | export default function handleSignals(): void; 2 | -------------------------------------------------------------------------------- /checkpoint/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@pika/pack", 3 | "description": "package building, reimagined.", 4 | "version": "0.4.0", 5 | "license": "MIT", 6 | "bin": { 7 | "pika-pack": "dist-node/index.bin.js" 8 | }, 9 | "files": [ 10 | "dist-*/", 11 | "bin/" 12 | ], 13 | "pika": true, 14 | "sideEffects": false, 15 | "homepage": "https://www.pikapkg.com/blog/introducing-pika-pack/", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/pikapkg/pack.git" 19 | }, 20 | "dependencies": { 21 | "@pika/cli": "latest", 22 | "@pika/types": "^0.6.0", 23 | "camelcase": "^4.0.0", 24 | "chalk": "^2.1.0", 25 | "commander": "^2.9.0", 26 | "file-uri-to-path": "^1.0.0", 27 | "glob": "^7.1.1", 28 | "import-from": "^3.0.0", 29 | "invariant": "^2.2.0", 30 | "is-builtin-module": "^2.0.0", 31 | "is-ci": "^1.0.10", 32 | "loud-rejection": "^1.2.0", 33 | "mkdirp": "^0.5.1", 34 | "np": "^5.0.2", 35 | "rimraf": "^2.5.0", 36 | "strip-ansi": "^4.0.0", 37 | "strip-bom": "^3.0.0", 38 | "validate-npm-package-license": "^3.0.4", 39 | "yargs-parser": "^13.1.1" 40 | }, 41 | "devDependencies": { 42 | "@pika/plugin-build-node": "^0.6.0", 43 | "@pika/plugin-simple-bin": "^0.6.0", 44 | "@pika/plugin-ts-standard-pkg": "^0.6.0", 45 | "prettier": "^1.15.3", 46 | "typescript": "^3.2.2" 47 | }, 48 | "engines": { 49 | "node": ">=8" 50 | }, 51 | "publishConfig": { 52 | "access": "public" 53 | }, 54 | "source": "dist-src/index.js", 55 | "types": "dist-types/index.d.ts", 56 | "main": "dist-node/index.js" 57 | } 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@pika/pack", 3 | "description": "package building, reimagined.", 4 | "version": "0.6.0", 5 | "license": "MIT", 6 | "homepage": "https://www.pikapkg.com/blog/introducing-pika-pack/", 7 | "engines": { 8 | "node": ">=8" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/pikapkg/pack.git" 13 | }, 14 | "scripts": { 15 | "format": "prettier --write src/**/*.ts", 16 | "build": "node checkpoint/dist-node/index.bin.js", 17 | "publish": "pika publish", 18 | "test": "node pkg/dist-node/index.bin.js", 19 | "version": "node pkg/dist-node/index.bin.js", 20 | "update-checkpoint": "rm -rf checkpoint/ && cp -r pkg/ checkpoint/" 21 | }, 22 | "@pika/pack": { 23 | "pipeline": [ 24 | [ 25 | "@pika/plugin-ts-standard-pkg" 26 | ], 27 | [ 28 | "@pika/plugin-build-node" 29 | ], 30 | [ 31 | "@pika/plugin-simple-bin", 32 | { 33 | "bin": "pika-pack", 34 | "minNodeVersion": 8 35 | } 36 | ] 37 | ] 38 | }, 39 | "publishConfig": { 40 | "access": "public" 41 | }, 42 | "dependencies": { 43 | "commander": "^6.2.1", 44 | "file-uri-to-path": "^2.0.0", 45 | "glob": "^7.1.6", 46 | "import-from": "^3.0.0", 47 | "invariant": "^2.2.4", 48 | "is-builtin-module": "^3.0.0", 49 | "is-ci": "^2.0.0", 50 | "kleur": "^4.1.3", 51 | "loud-rejection": "^2.2.0", 52 | "mkdirp": "^1.0.4", 53 | "np": "^7.1.0", 54 | "rimraf": "^3.0.2", 55 | "strip-ansi": "^6.0.0", 56 | "strip-bom": "^4.0.0", 57 | "validate-npm-package-license": "^3.0.4", 58 | "yargs-parser": "^20.2.4" 59 | }, 60 | "devDependencies": { 61 | "@pika/plugin-build-node": "^0.9.2", 62 | "@pika/plugin-simple-bin": "^0.9.2", 63 | "@pika/plugin-ts-standard-pkg": "^0.9.2", 64 | "@types/yargs-parser": "^20.2.0", 65 | "prettier": "^2.2.1", 66 | "typescript": "^4.1.3" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/commands/build.ts: -------------------------------------------------------------------------------- 1 | import {BuilderOptions} from '@pika/types'; 2 | import chalk from 'chalk'; 3 | import * as path from 'path'; 4 | import Config, {BuildFlags} from '../config.js'; 5 | import {DEFAULT_INDENT} from '../constants.js'; 6 | import {Reporter} from '../reporters/index.js'; 7 | import * as fs from '../util/fs.js'; 8 | import {generatePrettyManifest, generatePublishManifest} from '../util/normalize-manifest/for-publish.js'; 9 | 10 | export function hasWrapper(): boolean { 11 | return true; 12 | } 13 | 14 | export const examples = null; 15 | 16 | export class Build { 17 | constructor(flags: BuildFlags, config: Config, reporter: Reporter) { 18 | this.flags = flags; 19 | this.config = config; 20 | this.reporter = reporter; 21 | this.totalNum = 0; 22 | this.out = path.resolve(config.cwd, flags.out || 'pkg/'); 23 | if (this.out === this.config.cwd) { 24 | throw new Error('On publish, you cannot write to cwd because a package.json is created'); 25 | } 26 | } 27 | 28 | out: string; 29 | flags: BuildFlags; 30 | config: Config; 31 | reporter: Reporter; 32 | totalNum: number; 33 | 34 | async cleanup(): Promise { 35 | const {out} = this; 36 | await fs.unlink(path.join(out, '*')); 37 | } 38 | 39 | async init(isFull?: boolean): Promise { 40 | const {config, out, reporter, flags} = this; 41 | const {cwd} = config; 42 | const outPretty = path.relative(cwd, out) + path.sep; 43 | 44 | const manifest = await config.manifest; 45 | const {sourcemap} = manifest['@pika/pack'] || {sourcemap: true}; 46 | const distRunners = await config.getDistributions(); 47 | const builderConfig: Partial = { 48 | out, 49 | cwd, 50 | reporter: { 51 | info: (msg) => reporter.log(chalk.dim(` » ${msg}`)), 52 | warning: (msg) => reporter.log(chalk.yellow(` » ${msg}`)), 53 | success: (msg) => reporter.log(chalk.green(` » ${msg}`)), 54 | created: (filename: string, entrypoint?: string) => 55 | reporter.log( 56 | ` 📝 ${chalk.green(path.relative(cwd, filename))} ${entrypoint ? chalk.dim(`[${entrypoint}]`) : ''}`, 57 | ), 58 | }, 59 | isFull, 60 | manifest, 61 | src: { 62 | loc: path.join(out, 'dist-src'), 63 | entrypoint: path.join(out, 'dist-src', 'index.js'), 64 | // TODO: Deprecated, remove 65 | options: {}, 66 | // TODO: Deprecated, remove 67 | files: await (async (): Promise> => { 68 | const ignoreSet = new Set([]); 69 | ignoreSet.add('**/*/README.md'); 70 | const files = await fs.glob(`src/**/*`, { 71 | cwd, 72 | nodir: true, 73 | absolute: true, 74 | ignore: Array.from(ignoreSet).map((g) => path.join('src', g)), 75 | }); 76 | return files.filter((fileAbs) => !fileAbs.endsWith('.d.ts')); 77 | })(), 78 | }, 79 | }; 80 | const steps: Array<(curr: number, total: number) => Promise<{bailout: boolean} | void>> = []; 81 | steps.push(async (curr: number, total: number) => { 82 | this.reporter.step(curr, total, 'Validating source'); 83 | for (const [runner, options] of distRunners) { 84 | if (runner.validate) { 85 | const result = await runner.validate({ 86 | ...builderConfig, 87 | options: {sourcemap, ...options}, 88 | }); 89 | if (result instanceof Error) { 90 | throw result; 91 | } 92 | } 93 | } 94 | }); 95 | 96 | steps.push(async (curr: number, total: number) => { 97 | this.reporter.step(curr, total, `Preparing pipeline`); 98 | await this.cleanup(); 99 | reporter.log(` ❇️ ${chalk.green(outPretty)}`); 100 | for (const [runner, options] of distRunners) { 101 | await (runner.beforeBuild && 102 | runner.beforeBuild({ 103 | ...builderConfig, 104 | options: {sourcemap, ...options}, 105 | })); 106 | } 107 | }); 108 | 109 | if (distRunners.length === 0) { 110 | steps.push(async (curr: number, total: number) => { 111 | this.reporter.step( 112 | curr, 113 | total, 114 | `Pipeline is empty! See ${chalk.underline('https://github.com/pikapkg/pack')} for help getting started`, 115 | ); 116 | }); 117 | } 118 | 119 | for (const [runner, options] of distRunners) { 120 | steps.push(async (curr: number, total: number) => { 121 | this.reporter.step(curr, total, `Running ${chalk.bold(runner.name)}`); 122 | // return Promise.resolve( 123 | try { 124 | await (runner.beforeJob && 125 | runner.beforeJob({ 126 | ...builderConfig, 127 | options: {sourcemap, ...options}, 128 | })); 129 | await (runner.build && 130 | runner.build({ 131 | ...builderConfig, 132 | options: {sourcemap, ...options}, 133 | })); 134 | await (runner.afterJob && 135 | runner.afterJob({ 136 | ...builderConfig, 137 | options: {sourcemap, ...options}, 138 | })); 139 | } catch (err) { 140 | if (flags.force) { 141 | console.error(' ❗️ ', chalk.red(err.message), chalk.dim('--force, continuing...')); 142 | } else { 143 | console.error(' ❗️ ', chalk.red(err.message)); 144 | if (err.all) { 145 | console.error(' ❗️ ', chalk.bold('ERROR OUTPUT:')); 146 | console.error(err.all); 147 | } 148 | throw err; 149 | } 150 | } 151 | // ).catch(err => { 152 | // log(chalk.red(err.message)); 153 | // reporter.log( 154 | // reporter.lang("distFailed", runner.name, err.code, err.message), 155 | // { force: true } 156 | // ); 157 | // if (err.forceExit === true) { 158 | // reporter.log(reporter.lang("distExiting")); 159 | // throw err; 160 | // return; 161 | // } 162 | // reporter.log(reporter.lang("distContinuing")); 163 | // }); 164 | }); 165 | } 166 | steps.push(async (curr: number, total: number) => { 167 | this.reporter.step(curr, total, `Finalizing package`); 168 | for (const [runner, options] of distRunners) { 169 | await (runner.afterBuild && 170 | runner.afterBuild({ 171 | ...builderConfig, 172 | options: {sourcemap, ...options}, 173 | })); 174 | } 175 | 176 | if (await fs.exists(path.join(cwd, 'CHANGELOG'))) { 177 | fs.copyFile(path.join(cwd, 'CHANGELOG'), path.join(out, 'CHANGELOG')); 178 | reporter.log(chalk.dim(` » copying CHANGELOG...`)); 179 | } else if (await fs.exists(path.join(cwd, 'CHANGELOG.md'))) { 180 | fs.copyFile(path.join(cwd, 'CHANGELOG.md'), path.join(out, 'CHANGELOG.md')); 181 | reporter.log(chalk.dim(` » copying CHANGELOG.md...`)); 182 | } 183 | 184 | if (await fs.exists(path.join(cwd, 'LICENSE'))) { 185 | fs.copyFile(path.join(cwd, 'LICENSE'), path.join(out, 'LICENSE')); 186 | reporter.log(chalk.dim(` » copying LICENSE...`)); 187 | } else if (await fs.exists(path.join(cwd, 'LICENSE.md'))) { 188 | fs.copyFile(path.join(cwd, 'LICENSE.md'), path.join(out, 'LICENSE.md')); 189 | reporter.log(chalk.dim(` » copying LICENSE.md...`)); 190 | } 191 | 192 | if (await fs.exists(path.join(cwd, 'README'))) { 193 | fs.copyFile(path.join(cwd, 'README'), path.join(out, 'README')); 194 | reporter.log(chalk.dim(` » copying README...`)); 195 | } else if (await fs.exists(path.join(cwd, 'README.md'))) { 196 | fs.copyFile(path.join(cwd, 'README.md'), path.join(out, 'README.md')); 197 | reporter.log(chalk.dim(` » copying README.md...`)); 198 | } 199 | 200 | const publishManifest = await generatePublishManifest(config._manifest, config, distRunners); 201 | if (out === cwd) { 202 | reporter.log(`NEW MANIFEST:\n\n`); 203 | reporter.log(generatePrettyManifest(publishManifest)); 204 | reporter.log(`\n\n`); 205 | } else { 206 | await fs.writeFilePreservingEol( 207 | path.join(out, 'package.json'), 208 | JSON.stringify(publishManifest, null, DEFAULT_INDENT) + '\n', 209 | ); 210 | reporter.log(` 📝 ` + chalk.green(outPretty + 'package.json')); 211 | } 212 | 213 | reporter.log(` 📦 ` + chalk.green(outPretty)); 214 | }); 215 | let currentStep = 0; 216 | for (const step of steps) { 217 | await step(++currentStep, steps.length); 218 | } 219 | } 220 | } 221 | 222 | export async function run(config: Config, reporter: Reporter, flags: BuildFlags, args: Array): Promise { 223 | const isProduction = flags.publish; 224 | const builder = new Build(flags, config, reporter); 225 | await builder.init(isProduction); 226 | } 227 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | declare function __non_webpack_require__(m: string): any; 2 | 3 | import * as path from 'path'; 4 | import * as constants from './constants.js'; 5 | import {MessageError} from '@pika/types'; 6 | import {Manifest} from './types.js'; 7 | import * as fs from './util/fs.js'; 8 | import normalizeManifest from './util/normalize-manifest/index.js'; 9 | import BaseReporter from './reporters/base-reporter.js'; 10 | import executeLifecycleScript from './util/execute-lifecycle-script.js'; 11 | import importFrom from 'import-from'; 12 | 13 | export interface BuildFlags { 14 | publish?: boolean; 15 | out?: string; 16 | silent?: boolean; 17 | force?: boolean; 18 | } 19 | 20 | export interface GlobalFlags extends BuildFlags { 21 | cwd?: string; 22 | pipeline?: string; 23 | verbose?: boolean; 24 | json?: boolean; 25 | } 26 | 27 | export default class Config { 28 | cwd: string; 29 | reporter: BaseReporter; 30 | _manifest: any; 31 | manifest: Manifest; 32 | flags: GlobalFlags; 33 | 34 | constructor(reporter: BaseReporter, cwd: string, flags: GlobalFlags) { 35 | this.reporter = reporter; 36 | // Ensure the cwd is always an absolute path. 37 | this.cwd = path.resolve(cwd || process.cwd()); 38 | this.flags = flags; 39 | } 40 | 41 | async loadPackageManifest() { 42 | const loc = path.join(this.cwd, constants.NODE_PACKAGE_JSON); 43 | if (await fs.exists(loc)) { 44 | const info = await this.readJson(loc, fs.readJsonAndFile); 45 | this._manifest = {...info.object}; 46 | this.manifest = await normalizeManifest(info.object, this.cwd, this, true); 47 | return this.manifest; 48 | } else { 49 | return null; 50 | } 51 | } 52 | 53 | readJson(loc: string, factory: (filename: string) => Promise = fs.readJson): Promise { 54 | try { 55 | return factory(loc); 56 | } catch (err) { 57 | if (err instanceof SyntaxError) { 58 | throw new MessageError(this.reporter.lang('jsonError', loc, err.message)); 59 | } else { 60 | throw err; 61 | } 62 | } 63 | } 64 | 65 | async getDistributions(): Promise<[any, any][]> { 66 | const raw = this.manifest[`@pika/pack`] || {}; 67 | const override = this.flags.pipeline && JSON.parse(this.flags.pipeline); 68 | const cwd = this.cwd; 69 | function cleanRawDistObject(rawVal): false | [any, any] { 70 | if (Array.isArray(rawVal)) { 71 | let importStr = 72 | rawVal[0].startsWith('./') || rawVal[0].startsWith('../') ? path.join(cwd, rawVal[0]) : rawVal[0]; 73 | const importResult = importFrom(cwd, importStr) as any; 74 | return [{...importResult, name: rawVal[0]}, rawVal[1] || {}]; 75 | } 76 | if (typeof rawVal === 'string') { 77 | return [ 78 | { 79 | build: ({cwd}) => { 80 | return executeLifecycleScript({ 81 | // config: this, 82 | args: [], 83 | cwd, 84 | cmd: rawVal, 85 | isInteractive: false, 86 | }); 87 | }, 88 | }, 89 | {}, 90 | ]; 91 | } 92 | if (!rawVal) { 93 | throw new Error('Cannot be false'); 94 | } 95 | return false; 96 | } 97 | const pipeline: any[] = override || raw.pipeline || []; 98 | return pipeline.map(cleanRawDistObject).filter(Boolean) as [any, any][]; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | // import os from 'os'; 2 | // import * as path from 'path'; 3 | // import userHome from './util/user-home-dir.js'; 4 | // import {getCacheDir, getConfigDir, getDataDir} from './util/user-dirs.js'; 5 | 6 | export const DEPENDENCY_TYPES = ['devDependencies', 'dependencies', 'legacyDependencies']; 7 | // export const OWNED_DEPENDENCY_TYPES = ['devDependencies', 'dependencies', 'legacyDependencies']; 8 | 9 | export const RESOLUTIONS = 'resolutions'; 10 | export const MANIFEST_FIELDS = [RESOLUTIONS, ...DEPENDENCY_TYPES]; 11 | 12 | export const SUPPORTED_NODE_VERSIONS = '>=8.5.0'; 13 | 14 | // export const PIKA_REGISTRY = 'https://registry.npmjs.org'; 15 | // export const NPM_REGISTRY_RE = /https?:\/\/registry\.npmjs\.org/g; 16 | 17 | // export const PIKA_DOCS = 'https://yarnpkg.com/en/docs/cli/'; 18 | // export const PIKA_INSTALLER_SH = 'https://yarnpkg.com/install.sh'; 19 | // export const PIKA_INSTALLER_MSI = 'https://yarnpkg.com/latest.msi'; 20 | 21 | // export const SELF_UPDATE_VERSION_URL = 'https://www.pikapkg.com/downloads/latest-version'; 22 | 23 | // // cache version, bump whenever we make backwards incompatible changes 24 | // export const CACHE_VERSION = 3; 25 | 26 | // // lockfile version, bump whenever we make backwards incompatible changes 27 | // export const LOCKFILE_VERSION = 1; 28 | 29 | // // max amount of network requests to perform concurrently 30 | // export const NETWORK_CONCURRENCY = 8; 31 | 32 | // // HTTP timeout used when downloading packages 33 | // export const NETWORK_TIMEOUT = 30 * 1000; // in milliseconds 34 | 35 | // // max amount of child processes to execute concurrently 36 | export const CHILD_CONCURRENCY = 5; 37 | 38 | // export const REQUIRED_PACKAGE_KEYS = ['name', 'version', '_uid']; 39 | 40 | // function getPreferredCacheDirectories(): Array { 41 | // const preferredCacheDirectories = [getCacheDir()]; 42 | 43 | // if (process.getuid) { 44 | // // $FlowFixMe: process.getuid exists, dammit 45 | // preferredCacheDirectories.push(path.join(os.tmpdir(), `.pika-cache-${process.getuid()}`)); 46 | // } 47 | 48 | // preferredCacheDirectories.push(path.join(os.tmpdir(), `.pika-cache`)); 49 | 50 | // return preferredCacheDirectories; 51 | // } 52 | 53 | // export const PREFERRED_MODULE_CACHE_DIRECTORIES = getPreferredCacheDirectories(); 54 | // export const CONFIG_DIRECTORY = getConfigDir(); 55 | // export const DATA_DIRECTORY = getDataDir(); 56 | // export const LINK_REGISTRY_DIRECTORY = path.join(DATA_DIRECTORY, 'link'); 57 | // export const GLOBAL_MODULE_DIRECTORY = path.join(DATA_DIRECTORY, 'global'); 58 | 59 | // export const NODE_BIN_PATH = process.execPath; 60 | 61 | export const NODE_MODULES_FOLDER = 'node_modules'; 62 | export const NODE_PACKAGE_JSON = 'package.json'; 63 | 64 | // export const PNP_FILENAME = '.pnp'; 65 | 66 | // export const POSIX_GLOBAL_PREFIX = `${process.env.DESTDIR || ''}/usr/local`; 67 | // export const FALLBACK_GLOBAL_PREFIX = path.join(userHome, '.pika'); 68 | 69 | // export const META_FOLDER = '.pika-meta'; 70 | // export const INTEGRITY_FILENAME = '.pika-integrity'; 71 | // export const LOCKFILE_FILENAME = 'pika.lock'; 72 | // export const LEGACY_LOCKFILE_FILENAME = 'yarn.lock'; 73 | // export const METADATA_FILENAME = '.pika-metadata.json'; 74 | // export const TARBALL_FILENAME = '.pika-tarball.tgz'; 75 | // export const CLEAN_FILENAME = '.pikaclean'; 76 | 77 | // export const NPM_LOCK_FILENAME = 'package-lock.json'; 78 | // export const NPM_SHRINKWRAP_FILENAME = 'npm-shrinkwrap.json'; 79 | 80 | export const DEFAULT_INDENT = ' '; 81 | // export const SINGLE_INSTANCE_PORT = 31997; 82 | // export const SINGLE_INSTANCE_FILENAME = '.pika-single-instance'; 83 | 84 | export const ENV_PATH_KEY = getPathKey(process.platform, process.env); 85 | 86 | export function getPathKey(platform: string, env: NodeJS.ProcessEnv): string { 87 | let pathKey = 'PATH'; 88 | 89 | // windows calls its path "Path" usually, but this is not guaranteed. 90 | if (platform === 'win32') { 91 | pathKey = 'Path'; 92 | 93 | for (const key in env) { 94 | if (key.toLowerCase() === 'path') { 95 | pathKey = key; 96 | } 97 | } 98 | } 99 | 100 | return pathKey; 101 | } 102 | 103 | // export const VERSION_COLOR_SCHEME: {[key: string]: VersionColor} = { 104 | // major: 'red', 105 | // premajor: 'red', 106 | // minor: 'yellow', 107 | // preminor: 'yellow', 108 | // patch: 'green', 109 | // prepatch: 'green', 110 | // prerelease: 'red', 111 | // unchanged: 'white', 112 | // unknown: 'red', 113 | // }; 114 | 115 | // export type VersionColor = 'red' | 'yellow' | 'green' | 'white'; 116 | 117 | // export type RequestHint = 'dev' | 'optional' | 'resolution' | 'workspaces'; 118 | -------------------------------------------------------------------------------- /src/errors.ts: -------------------------------------------------------------------------------- 1 | import {MessageError} from '@pika/types'; 2 | 3 | export class ProcessSpawnError extends MessageError { 4 | constructor(msg: string, code?: string, process?: string) { 5 | super(msg); 6 | this.code = code; 7 | this.process = process; 8 | } 9 | 10 | code?: string; 11 | process?: string; 12 | } 13 | 14 | export class SecurityError extends MessageError {} 15 | 16 | export class ProcessTermError extends MessageError { 17 | EXIT_CODE?: number; 18 | EXIT_SIGNAL?: string; 19 | } 20 | 21 | export class ResponseError extends Error { 22 | constructor(msg: string, responseCode: number) { 23 | super(msg); 24 | this.responseCode = responseCode; 25 | } 26 | 27 | responseCode: number; 28 | } 29 | 30 | export class OneTimePasswordError extends Error {} 31 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as kleur from 'kleur'; 3 | import * as fs from 'fs'; 4 | import invariant from 'invariant'; 5 | import loudRejection from 'loud-rejection'; 6 | 7 | import {ConsoleReporter, JSONReporter} from './reporters/index.js'; 8 | import * as buildCommand from './commands/build.js'; 9 | import {MessageError} from '@pika/types'; 10 | import Config, {GlobalFlags} from './config.js'; 11 | import handleSignals from './util/signal-handler.js'; 12 | import {boolifyWithDefault} from './util/conversion.js'; 13 | import map from './util/map.js'; 14 | import stripBOM from 'strip-bom'; 15 | import uri2path from 'file-uri-to-path'; 16 | import yargs from 'yargs-parser'; 17 | 18 | // @ts-ignore 19 | const currentFilename = uri2path(import.meta.url); 20 | function getVersion() { 21 | const packageJsonContent = fs.readFileSync(path.resolve(currentFilename, '../../package.json'), {encoding: 'utf-8'}); 22 | const {version} = map(JSON.parse(stripBOM(packageJsonContent))); 23 | return version; 24 | } 25 | 26 | function printHelp() { 27 | console.log( 28 | ` 29 | ${kleur.bold(`@pika/pack`)} - Build npm packages without the mess. 30 | ${kleur.bold('Options:')} 31 | --cwd Set the current working directory. 32 | --out Set the output directory. Defaults to "pkg/". 33 | --pipeline Set a build pipeline via JSON string. 34 | --force Continue with the build when a build plugin fails or throws an exception. 35 | --json Log output as JSON. 36 | --verbose Log additional debugging information. 37 | --silent Log almost nothing. 38 | --help Print help. 39 | --version, -v Print version. 40 | `.trim(), 41 | ); 42 | } 43 | 44 | export async function cli(args: string[]) { 45 | const version = getVersion(); 46 | loudRejection(); 47 | handleSignals(); 48 | 49 | // Handle special flags 50 | if (args.find((arg) => arg === '--version' || arg === '-v')) { 51 | console.log(version.trim()); 52 | process.exitCode = 0; 53 | return; 54 | } 55 | if (args.find((arg) => arg === '--help')) { 56 | printHelp(); 57 | process.exitCode = 0; 58 | return; 59 | } 60 | 61 | // Handle the legacy CLI interface 62 | if (args[2] === 'publish') { 63 | console.log(`The publish flow has moved to the @pika/cli package (included with this package). 64 | Update your publish script to: ${kleur.bold('pika publish [flags]')} 65 | `); 66 | process.exitCode = 1; 67 | return; 68 | } 69 | if (args[2] === 'build') { 70 | console.log( 71 | kleur.yellow( 72 | `Note: This CLI was recently deprecated. Update your build script to: ${kleur.bold('pika build [flags]')}`, 73 | ), 74 | ); 75 | args.splice(2, 1); 76 | } 77 | 78 | const flags = yargs(args) as GlobalFlags; 79 | const cwd = flags.cwd || process.cwd(); 80 | const Reporter = flags.json ? JSONReporter : ConsoleReporter; 81 | const reporter = new Reporter({ 82 | emoji: true, 83 | verbose: flags.verbose, 84 | isSilent: boolifyWithDefault(process.env.PIKA_SILENT, false) || flags.silent, 85 | }); 86 | 87 | const exit = (exitCode: any = 0) => { 88 | process.exitCode = exitCode; 89 | reporter.close(); 90 | }; 91 | 92 | const command = buildCommand; 93 | reporter.initPeakMemoryCounter(); 94 | const outputWrapperEnabled = boolifyWithDefault(process.env.PIKA_WRAP_OUTPUT, true); 95 | const shouldWrapOutput = outputWrapperEnabled && !flags.json && command.hasWrapper(); 96 | if (shouldWrapOutput) { 97 | reporter.header({name: '@pika/pack', version}); 98 | } 99 | 100 | const run = (): Promise => { 101 | invariant(command, 'missing command'); 102 | 103 | return command.run(config, reporter, flags, args).then((exitCode) => { 104 | if (shouldWrapOutput) { 105 | reporter.footer(false); 106 | } 107 | return exitCode; 108 | }); 109 | }; 110 | 111 | function onUnexpectedError(err: Error) { 112 | function indent(str: string): string { 113 | return '\n ' + str.trim().split('\n').join('\n '); 114 | } 115 | 116 | const log = []; 117 | log.push(`Arguments: ${indent(process.argv.join(' '))}`); 118 | log.push(`PATH: ${indent(process.env.PATH || 'undefined')}`); 119 | log.push(`Pika version: ${indent(version)}`); 120 | log.push(`Node version: ${indent(process.versions.node)}`); 121 | log.push(`Platform: ${indent(process.platform + ' ' + process.arch)}`); 122 | 123 | log.push(`Trace: ${indent(err.stack)}`); 124 | reporter.error(reporter.lang('unexpectedError', err.message)); 125 | } 126 | 127 | const config = new Config(reporter, cwd, flags); 128 | await config.loadPackageManifest(); 129 | 130 | try { 131 | // option "no-progress" stored in pika config 132 | const noProgressConfig = false; //config.registries.pika.getOption('no-progress'); 133 | 134 | if (noProgressConfig) { 135 | reporter.disableProgress(); 136 | } 137 | 138 | // verbose logs outputs process.uptime() with this line we can sync uptime to absolute time on the computer 139 | reporter.verbose(`current time: ${new Date().toISOString()}`); 140 | return run().then(exit); 141 | } catch (err) { 142 | reporter.verbose(err.stack); 143 | 144 | if (err instanceof MessageError) { 145 | reporter.error(err.message); 146 | } else { 147 | onUnexpectedError(err); 148 | } 149 | 150 | return exit(1); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/reporters/base-reporter.ts: -------------------------------------------------------------------------------- 1 | import {ReporterSpinnerSet, Trees, Stdout, Stdin, Package, ReporterSpinner} from './types'; 2 | import {LanguageKeys} from './lang/en.js'; 3 | import {Formatter} from './format.js'; 4 | 5 | import {defaultFormatter} from './format.js'; 6 | import * as languages from './lang/index.js'; 7 | import * as isCI from 'is-ci'; 8 | import * as os from 'os'; 9 | 10 | import * as util from 'util'; 11 | import {EventEmitter} from 'events'; 12 | 13 | type Language = keyof typeof languages; 14 | 15 | export type ReporterOptions = { 16 | verbose?: boolean; 17 | language?: Language; 18 | stdout?: Stdout; 19 | stderr?: Stdout; 20 | stdin?: Stdin; 21 | emoji?: boolean; 22 | noProgress?: boolean; 23 | silent?: boolean; 24 | isSilent?: boolean; 25 | nonInteractive?: boolean; 26 | }; 27 | 28 | export function stringifyLangArgs(args: Array): Array { 29 | return args.map(function (val): string { 30 | if (val != null && val.inspect) { 31 | return val.inspect(); 32 | } else { 33 | try { 34 | const str = JSON.stringify(val) || val + ''; 35 | // should match all literal line breaks and 36 | // "u001b" that follow an odd number of backslashes and convert them to ESC 37 | // we do this because the JSON.stringify process has escaped these characters 38 | return str 39 | .replace(/((?:^|[^\\])(?:\\{2})*)\\u001[bB]/g, '$1\u001b') 40 | .replace(/[\\]r[\\]n|([\\])?[\\]n/g, (match, precededBacklash) => { 41 | // precededBacklash not null when "\n" is preceded by a backlash ("\\n") 42 | // match will be "\\n" and we don't replace it with os.EOL 43 | return precededBacklash ? match : os.EOL; 44 | }); 45 | } catch (e) { 46 | return util.inspect(val); 47 | } 48 | } 49 | }); 50 | } 51 | 52 | export default class BaseReporter { 53 | constructor(opts: ReporterOptions = {}) { 54 | const lang = 'en'; 55 | this.language = lang; 56 | this.stdout = opts.stdout || process.stdout; 57 | this.stderr = opts.stderr || process.stderr; 58 | this.stdin = opts.stdin || this._getStandardInput(); 59 | this.emoji = !!opts.emoji; 60 | this.nonInteractive = !!opts.nonInteractive; 61 | this.noProgress = !!opts.noProgress || isCI; 62 | this.isVerbose = !!opts.verbose; 63 | 64 | // @ts-ignore 65 | this.isTTY = this.stdout.isTTY; 66 | 67 | this.peakMemory = 0; 68 | this.startTime = Date.now(); 69 | this.format = defaultFormatter; 70 | } 71 | 72 | formatter: Formatter; 73 | language: Language; 74 | stdout: Stdout; 75 | stderr: Stdout; 76 | stdin: Stdin; 77 | isTTY: boolean; 78 | emoji: boolean; 79 | noProgress: boolean; 80 | isVerbose: boolean; 81 | isSilent: boolean; 82 | nonInteractive: boolean; 83 | format: Formatter; 84 | 85 | peakMemoryInterval?: NodeJS.Timer; 86 | peakMemory: number; 87 | startTime: number; 88 | 89 | lang(key: LanguageKeys, ...args: Array): string { 90 | const msg = languages[this.language][key] || languages.en[key]; 91 | if (!msg) { 92 | throw new ReferenceError(`No message defined for language key ${key}`); 93 | } 94 | 95 | // stringify args 96 | const stringifiedArgs = stringifyLangArgs(args); 97 | 98 | // replace $0 placeholders with args 99 | return msg.replace(/\$(\d+)/g, (str, i: number) => { 100 | return stringifiedArgs[i]; 101 | }); 102 | } 103 | 104 | /** 105 | * `stringifyLangArgs` run `JSON.stringify` on strings too causing 106 | * them to appear quoted. This marks them as "raw" and prevents 107 | * the quoting and escaping 108 | */ 109 | rawText(str: string): {inspect(): string} { 110 | return { 111 | inspect(): string { 112 | return str; 113 | }, 114 | }; 115 | } 116 | 117 | verbose(msg: string) { 118 | if (this.isVerbose) { 119 | this._verbose(msg); 120 | } 121 | } 122 | 123 | verboseInspect(val: any) { 124 | if (this.isVerbose) { 125 | this._verboseInspect(val); 126 | } 127 | } 128 | 129 | _verbose(msg: string) {} 130 | _verboseInspect(val: any) {} 131 | 132 | _getStandardInput(): Stdin { 133 | let standardInput; 134 | 135 | // Accessing stdin in a win32 headless process (e.g., Visual Studio) may throw an exception. 136 | try { 137 | standardInput = process.stdin; 138 | } catch (e) { 139 | console.warn(e.message); 140 | delete process.stdin; 141 | // @ts-ignore 142 | process.stdin = new EventEmitter(); 143 | standardInput = process.stdin; 144 | } 145 | 146 | return standardInput; 147 | } 148 | 149 | initPeakMemoryCounter() { 150 | this.checkPeakMemory(); 151 | this.peakMemoryInterval = setInterval(() => { 152 | this.checkPeakMemory(); 153 | }, 1000); 154 | // $FlowFixMe: Node's setInterval returns a Timeout, not a Number 155 | this.peakMemoryInterval.unref(); 156 | } 157 | 158 | checkPeakMemory() { 159 | const {heapTotal} = process.memoryUsage(); 160 | if (heapTotal > this.peakMemory) { 161 | this.peakMemory = heapTotal; 162 | } 163 | } 164 | 165 | close() { 166 | if (this.peakMemoryInterval) { 167 | clearInterval(this.peakMemoryInterval); 168 | this.peakMemoryInterval = null; 169 | } 170 | } 171 | 172 | getTotalTime(): number { 173 | return Date.now() - this.startTime; 174 | } 175 | 176 | // TODO 177 | list(key: string, items: Array, hints?: Object) {} 178 | 179 | // Outputs basic tree structure to console 180 | tree(key: string, obj: Trees, {force = false}: {force?: boolean} = {}) {} 181 | 182 | // called whenever we begin a step in the CLI. 183 | step(current: number, total: number, message: string, emoji?: string) {} 184 | 185 | // a error message has been triggered. this however does not always meant an abrupt 186 | // program end. 187 | error(message: string) {} 188 | 189 | // an info message has been triggered. this provides things like stats and diagnostics. 190 | info(message: string) {} 191 | 192 | // a warning message has been triggered. 193 | warn(message: string) {} 194 | 195 | // a success message has been triggered. 196 | success(message: string) {} 197 | 198 | // a simple log message 199 | // TODO: rethink the {force} parameter. In the meantime, please don't use it (cf comments in #4143). 200 | log(message: string, {force = false}: {force?: boolean} = {}) {} 201 | 202 | // a shell command has been executed 203 | command(command: string) {} 204 | 205 | // inspect and pretty-print any value 206 | inspect(value: any) {} 207 | 208 | // the screen shown at the very start of the CLI 209 | header(pkg: Package) {} 210 | 211 | // the screen shown at the very end of the CLI 212 | footer(showPeakMemory: boolean) {} 213 | 214 | // a table structure 215 | table(head: Array, body: Array>) {} 216 | 217 | // render an activity spinner and return a function that will trigger an update 218 | activity(): ReporterSpinner { 219 | return { 220 | tick(name: string) {}, 221 | end() {}, 222 | }; 223 | } 224 | 225 | // 226 | activitySet(total: number, workers: number): ReporterSpinnerSet { 227 | return { 228 | spinners: Array(workers).fill({ 229 | clear() {}, 230 | setPrefix() {}, 231 | tick() {}, 232 | end() {}, 233 | }), 234 | end() {}, 235 | }; 236 | } 237 | 238 | // render a progress bar and return a function which when called will trigger an update 239 | progress(total: number): () => void { 240 | return function () {}; 241 | } 242 | 243 | // utility function to disable progress bar 244 | disableProgress() { 245 | this.noProgress = true; 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/reporters/console/helpers/tree-helper.ts: -------------------------------------------------------------------------------- 1 | // types 2 | import {Trees} from '../../types.js'; 3 | 4 | export type FormattedOutput = { 5 | prefix: string; 6 | hint: any; 7 | color: string; 8 | name: string; 9 | formatter: any; 10 | }; 11 | 12 | // public 13 | export function sortTrees(trees: Trees): Trees { 14 | return trees.sort(function (tree1, tree2): number { 15 | return tree1.name.localeCompare(tree2.name); 16 | }); 17 | } 18 | 19 | export function recurseTree(tree: Trees, prefix: string, recurseFunc: Function) { 20 | const treeLen = tree.length; 21 | const treeEnd = treeLen - 1; 22 | for (let i = 0; i < treeLen; i++) { 23 | const atEnd = i === treeEnd; 24 | recurseFunc(tree[i], prefix + getLastIndentChar(atEnd), prefix + getNextIndentChar(atEnd)); 25 | } 26 | } 27 | 28 | export function getFormattedOutput(fmt: FormattedOutput): string { 29 | const item = formatColor(fmt.color, fmt.name, fmt.formatter); 30 | const suffix = getSuffix(fmt.hint, fmt.formatter); 31 | return `${fmt.prefix}─ ${item}${suffix}\n`; 32 | } 33 | 34 | function getNextIndentChar(end: boolean): string { 35 | return end ? ' ' : '│ '; 36 | } 37 | 38 | function getLastIndentChar(end: boolean): string { 39 | return end ? '└' : '├'; 40 | } 41 | 42 | function getSuffix(hint: any, formatter: any): string { 43 | return hint ? ` (${formatter.grey(hint)})` : ''; 44 | } 45 | 46 | function formatColor(color: string, strToFormat: string, formatter: any): string { 47 | return color ? formatter[color](strToFormat) : strToFormat; 48 | } 49 | -------------------------------------------------------------------------------- /src/reporters/console/progress-bar.ts: -------------------------------------------------------------------------------- 1 | import {Stdout} from '../types.js'; 2 | import {clearLine, toStartOfLine} from './util.js'; 3 | 4 | export default class ProgressBar { 5 | constructor(total: number, stdout: Stdout = process.stderr, callback?: (progressBar: ProgressBar) => void) { 6 | this.stdout = stdout; 7 | this.total = total; 8 | this.chars = ProgressBar.bars[0]; 9 | this.delay = 60; 10 | this.curr = 0; 11 | this._callback = callback; 12 | clearLine(stdout); 13 | } 14 | 15 | stdout: Stdout; 16 | curr: number; 17 | total: number; 18 | width: number; 19 | chars: [string, string]; 20 | delay: number; 21 | id?: NodeJS.Timeout; 22 | _callback?: (progressBar: ProgressBar) => void; 23 | 24 | static bars: [string, string][] = [['#', '-']]; 25 | 26 | tick() { 27 | if (this.curr >= this.total) { 28 | return; 29 | } 30 | 31 | this.curr++; 32 | 33 | // schedule render 34 | if (!this.id) { 35 | this.id = setTimeout((): void => this.render(), this.delay); 36 | } 37 | } 38 | 39 | cancelTick() { 40 | if (this.id) { 41 | clearTimeout(this.id); 42 | this.id = null; 43 | } 44 | } 45 | 46 | stop() { 47 | // "stop" by setting current to end so `tick` becomes noop 48 | this.curr = this.total; 49 | 50 | this.cancelTick(); 51 | clearLine(this.stdout); 52 | if (this._callback) { 53 | this._callback(this); 54 | } 55 | } 56 | 57 | render() { 58 | // clear throttle 59 | this.cancelTick(); 60 | 61 | let ratio = this.curr / this.total; 62 | ratio = Math.min(Math.max(ratio, 0), 1); 63 | 64 | // progress without bar 65 | let bar = ` ${this.curr}/${this.total}`; 66 | 67 | // calculate size of actual bar 68 | // $FlowFixMe: investigate process.stderr.columns flow error 69 | // @ts-ignore 70 | const availableSpace = Math.max(0, this.stdout.columns - bar.length - 3); 71 | const width = Math.min(this.total, availableSpace); 72 | const completeLength = Math.round(width * ratio); 73 | const complete = this.chars[0].repeat(completeLength); 74 | const incomplete = this.chars[1].repeat(width - completeLength); 75 | bar = `[${complete}${incomplete}]${bar}`; 76 | 77 | toStartOfLine(this.stdout); 78 | this.stdout.write(bar); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/reporters/console/spinner-progress.ts: -------------------------------------------------------------------------------- 1 | import {Stdout} from '../types.js'; 2 | import {writeOnNthLine, clearNthLine} from './util.js'; 3 | 4 | export default class Spinner { 5 | constructor(stdout: Stdout = process.stderr, lineNumber: number = 0) { 6 | this.current = 0; 7 | this.prefix = ''; 8 | this.lineNumber = lineNumber; 9 | this.stdout = stdout; 10 | this.delay = 60; 11 | this.chars = Spinner.spinners[28].split(''); 12 | this.text = ''; 13 | this.id = null; 14 | } 15 | 16 | stdout: Stdout; 17 | prefix: string; 18 | current: number; 19 | lineNumber: number; 20 | delay: number; 21 | chars: Array; 22 | text: string; 23 | id?: NodeJS.Timeout; 24 | 25 | static spinners: Array = [ 26 | '|/-\\', 27 | '⠂-–—–-', 28 | '◐◓◑◒', 29 | '◴◷◶◵', 30 | '◰◳◲◱', 31 | '▖▘▝▗', 32 | '■□▪▫', 33 | '▌▀▐▄', 34 | '▉▊▋▌▍▎▏▎▍▌▋▊▉', 35 | '▁▃▄▅▆▇█▇▆▅▄▃', 36 | '←↖↑↗→↘↓↙', 37 | '┤┘┴└├┌┬┐', 38 | '◢◣◤◥', 39 | '.oO°Oo.', 40 | '.oO@*', 41 | '🌍🌎🌏', 42 | '◡◡ ⊙⊙ ◠◠', 43 | '☱☲☴', 44 | '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏', 45 | '⠋⠙⠚⠞⠖⠦⠴⠲⠳⠓', 46 | '⠄⠆⠇⠋⠙⠸⠰⠠⠰⠸⠙⠋⠇⠆', 47 | '⠋⠙⠚⠒⠂⠂⠒⠲⠴⠦⠖⠒⠐⠐⠒⠓⠋', 48 | '⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠴⠲⠒⠂⠂⠒⠚⠙⠉⠁', 49 | '⠈⠉⠋⠓⠒⠐⠐⠒⠖⠦⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈', 50 | '⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈', 51 | '⢄⢂⢁⡁⡈⡐⡠', 52 | '⢹⢺⢼⣸⣇⡧⡗⡏', 53 | '⣾⣽⣻⢿⡿⣟⣯⣷', 54 | '⠁⠂⠄⡀⢀⠠⠐⠈', 55 | ]; 56 | setPrefix(prefix: string) { 57 | this.prefix = prefix; 58 | } 59 | setText(text: string) { 60 | this.text = text; 61 | } 62 | start() { 63 | this.current = 0; 64 | this.render(); 65 | } 66 | render() { 67 | if (this.id) { 68 | clearTimeout(this.id); 69 | } 70 | // build line ensuring we don't wrap to the next line 71 | let msg = `${this.prefix}${this.chars[this.current]} ${this.text}`; 72 | // @ts-ignore 73 | const columns = typeof this.stdout.columns === 'number' ? this.stdout.columns : 100; 74 | msg = msg.slice(0, columns); 75 | writeOnNthLine(this.stdout, this.lineNumber, msg); 76 | this.current = ++this.current % this.chars.length; 77 | this.id = setTimeout((): void => this.render(), this.delay); 78 | } 79 | stop() { 80 | if (this.id) { 81 | clearTimeout(this.id); 82 | this.id = null; 83 | } 84 | clearNthLine(this.stdout, this.lineNumber); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/reporters/console/util.ts: -------------------------------------------------------------------------------- 1 | import * as tty from 'tty'; 2 | import {Stdout} from '../types.js'; 3 | 4 | import * as readline from 'readline'; 5 | import chalk from 'chalk'; 6 | 7 | const CLEAR_WHOLE_LINE = 0; 8 | const CLEAR_RIGHT_OF_CURSOR = 1; 9 | 10 | export function clearLine(stdout: Stdout) { 11 | if (!chalk.supportsColor) { 12 | if (stdout instanceof tty.WriteStream) { 13 | if (stdout.columns > 0) { 14 | stdout.write(`\r${' '.repeat(stdout.columns - 1)}`); 15 | } 16 | stdout.write(`\r`); 17 | } 18 | return; 19 | } 20 | 21 | readline.clearLine(stdout, CLEAR_WHOLE_LINE); 22 | readline.cursorTo(stdout, 0); 23 | } 24 | 25 | export function toStartOfLine(stdout: Stdout) { 26 | if (!chalk.supportsColor) { 27 | stdout.write('\r'); 28 | return; 29 | } 30 | 31 | readline.cursorTo(stdout, 0); 32 | } 33 | 34 | export function writeOnNthLine(stdout: Stdout, n: number, msg: string) { 35 | if (!chalk.supportsColor) { 36 | return; 37 | } 38 | 39 | if (n == 0) { 40 | readline.cursorTo(stdout, 0); 41 | stdout.write(msg); 42 | readline.clearLine(stdout, CLEAR_RIGHT_OF_CURSOR); 43 | return; 44 | } 45 | readline.cursorTo(stdout, 0); 46 | readline.moveCursor(stdout, 0, -n); 47 | stdout.write(msg); 48 | readline.clearLine(stdout, CLEAR_RIGHT_OF_CURSOR); 49 | readline.cursorTo(stdout, 0); 50 | readline.moveCursor(stdout, 0, n); 51 | } 52 | 53 | export function clearNthLine(stdout: Stdout, n: number) { 54 | if (!chalk.supportsColor) { 55 | return; 56 | } 57 | 58 | if (n == 0) { 59 | clearLine(stdout); 60 | return; 61 | } 62 | readline.cursorTo(stdout, 0); 63 | readline.moveCursor(stdout, 0, -n); 64 | readline.clearLine(stdout, CLEAR_WHOLE_LINE); 65 | readline.moveCursor(stdout, 0, n); 66 | } 67 | -------------------------------------------------------------------------------- /src/reporters/format.ts: -------------------------------------------------------------------------------- 1 | function formatFunction(...strs: Array): string { 2 | return strs.join(' '); 3 | } 4 | 5 | export const defaultFormatter = { 6 | bold: formatFunction, 7 | dim: formatFunction, 8 | italic: formatFunction, 9 | underline: formatFunction, 10 | inverse: formatFunction, 11 | strikethrough: formatFunction, 12 | black: formatFunction, 13 | red: formatFunction, 14 | green: formatFunction, 15 | yellow: formatFunction, 16 | blue: formatFunction, 17 | magenta: formatFunction, 18 | cyan: formatFunction, 19 | white: formatFunction, 20 | gray: formatFunction, 21 | grey: formatFunction, 22 | stripColor: formatFunction, 23 | }; 24 | 25 | type FormatFunction = (...strs: Array) => string; 26 | 27 | export type FormatKeys = keyof typeof defaultFormatter; 28 | 29 | export type Formatter = { 30 | bold: FormatFunction; 31 | dim: FormatFunction; 32 | italic: FormatFunction; 33 | underline: FormatFunction; 34 | inverse: FormatFunction; 35 | strikethrough: FormatFunction; 36 | black: FormatFunction; 37 | red: FormatFunction; 38 | green: FormatFunction; 39 | yellow: FormatFunction; 40 | blue: FormatFunction; 41 | magenta: FormatFunction; 42 | cyan: FormatFunction; 43 | white: FormatFunction; 44 | gray: FormatFunction; 45 | grey: FormatFunction; 46 | stripColor: FormatFunction; 47 | }; 48 | -------------------------------------------------------------------------------- /src/reporters/index.ts: -------------------------------------------------------------------------------- 1 | export {default as ConsoleReporter} from './console/console-reporter'; 2 | export {default as JSONReporter} from './json-reporter'; 3 | export {default as NoopReporter} from './noop-reporter'; 4 | export {default as Reporter} from './base-reporter'; 5 | -------------------------------------------------------------------------------- /src/reporters/json-reporter.ts: -------------------------------------------------------------------------------- 1 | import {ReporterSpinnerSet, Trees, ReporterSpinner} from './types.js'; 2 | import BaseReporter from './base-reporter.js'; 3 | 4 | export default class JSONReporter extends BaseReporter { 5 | constructor(opts?: Object) { 6 | super(opts); 7 | 8 | this._activityId = 0; 9 | this._progressId = 0; 10 | } 11 | 12 | _activityId: number; 13 | _progressId: number; 14 | 15 | _dump(type: string, data: any, error?: boolean) { 16 | let stdout = this.stdout; 17 | if (error) { 18 | stdout = this.stderr; 19 | } 20 | stdout.write(`${JSON.stringify({type, data})}\n`); 21 | } 22 | 23 | _verbose(msg: string) { 24 | this._dump('verbose', msg); 25 | } 26 | 27 | list(type: string, items: Array, hints?: Object) { 28 | this._dump('list', {type, items, hints}); 29 | } 30 | 31 | tree(type: string, trees: Trees) { 32 | this._dump('tree', {type, trees}); 33 | } 34 | 35 | step(current: number, total: number, message: string) { 36 | this._dump('step', {message, current, total}); 37 | } 38 | 39 | inspect(value: any) { 40 | this._dump('inspect', value); 41 | } 42 | 43 | footer(showPeakMemory: boolean) { 44 | this._dump('finished', this.getTotalTime()); 45 | } 46 | 47 | log(msg: string) { 48 | this._dump('log', msg); 49 | } 50 | 51 | command(msg: string) { 52 | this._dump('command', msg); 53 | } 54 | 55 | table(head: Array, body: Array>) { 56 | this._dump('table', {head, body}); 57 | } 58 | 59 | success(msg: string) { 60 | this._dump('success', msg); 61 | } 62 | 63 | error(msg: string) { 64 | this._dump('error', msg, true); 65 | } 66 | 67 | warn(msg: string) { 68 | this._dump('warning', msg, true); 69 | } 70 | 71 | info(msg: string) { 72 | this._dump('info', msg); 73 | } 74 | 75 | activitySet(total: number, workers: number): ReporterSpinnerSet { 76 | if (!this.isTTY || this.noProgress) { 77 | return super.activitySet(total, workers); 78 | } 79 | 80 | const id = this._activityId++; 81 | this._dump('activitySetStart', {id, total, workers}); 82 | 83 | const spinners = []; 84 | for (let i = 0; i < workers; i++) { 85 | let current = 0; 86 | let header = ''; 87 | 88 | spinners.push({ 89 | clear() {}, 90 | setPrefix(_current: number, _header: string) { 91 | current = _current; 92 | header = _header; 93 | }, 94 | tick: (msg) => { 95 | this._dump('activitySetTick', { 96 | id, 97 | header, 98 | current, 99 | worker: i, 100 | message: msg, 101 | }); 102 | }, 103 | end() {}, 104 | }); 105 | } 106 | 107 | return { 108 | spinners, 109 | end: () => { 110 | this._dump('activitySetEnd', {id}); 111 | }, 112 | }; 113 | } 114 | 115 | activity(): ReporterSpinner { 116 | return this._activity({}); 117 | } 118 | 119 | _activity(data: Object): ReporterSpinner { 120 | if (!this.isTTY || this.noProgress) { 121 | return { 122 | tick() {}, 123 | end() {}, 124 | }; 125 | } 126 | 127 | const id = this._activityId++; 128 | this._dump('activityStart', {id, ...data}); 129 | 130 | return { 131 | tick: (name: string) => { 132 | this._dump('activityTick', {id, name}); 133 | }, 134 | 135 | end: () => { 136 | this._dump('activityEnd', {id}); 137 | }, 138 | }; 139 | } 140 | 141 | progress(total: number): () => void { 142 | if (this.noProgress) { 143 | return function () { 144 | // noop 145 | }; 146 | } 147 | 148 | const id = this._progressId++; 149 | let current = 0; 150 | this._dump('progressStart', {id, total}); 151 | 152 | return () => { 153 | current++; 154 | this._dump('progressTick', {id, current}); 155 | 156 | if (current === total) { 157 | this._dump('progressFinish', {id}); 158 | } 159 | }; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/reporters/lang/index.ts: -------------------------------------------------------------------------------- 1 | import en from './en.js'; 2 | export {en}; 3 | -------------------------------------------------------------------------------- /src/reporters/noop-reporter.ts: -------------------------------------------------------------------------------- 1 | import BaseReporter from './base-reporter.js'; 2 | import {LanguageKeys} from './lang/en.js'; 3 | import {Package, ReporterSpinner, ReporterSpinnerSet, Trees} from './types.js'; 4 | 5 | export default class NoopReporter extends BaseReporter { 6 | lang(key: LanguageKeys, ...args: Array): string { 7 | return 'do nothing'; 8 | } 9 | verbose(msg: string) {} 10 | verboseInspect(val: any) {} 11 | initPeakMemoryCounter() {} 12 | checkPeakMemory() {} 13 | close() {} 14 | getTotalTime(): number { 15 | return 0; 16 | } 17 | list(key: string, items: Array, hints?: Object) {} 18 | tree(key: string, obj: Trees) {} 19 | step(current: number, total: number, message: string, emoji?: string) {} 20 | error(message: string) {} 21 | info(message: string) {} 22 | warn(message: string) {} 23 | success(message: string) {} 24 | log(message: string) {} 25 | command(command: string) {} 26 | inspect(value: any) {} 27 | header(pkg: Package) {} 28 | footer(showPeakMemory: boolean) {} 29 | table(head: Array, body: Array>) {} 30 | 31 | activity(): ReporterSpinner { 32 | return { 33 | tick(name: string) {}, 34 | end() {}, 35 | }; 36 | } 37 | 38 | activitySet(total: number, workers: number): ReporterSpinnerSet { 39 | return { 40 | spinners: Array(workers).fill({ 41 | clear() {}, 42 | setPrefix() {}, 43 | tick() {}, 44 | end() {}, 45 | }), 46 | end() {}, 47 | }; 48 | } 49 | 50 | progress(total: number): () => void { 51 | return function () {}; 52 | } 53 | 54 | disableProgress() { 55 | this.noProgress = true; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/reporters/types.ts: -------------------------------------------------------------------------------- 1 | import {Writable, Readable} from 'stream'; 2 | import {WriteStream, ReadStream} from 'fs'; 3 | 4 | export type Stdout = Writable | WriteStream; 5 | export type Stdin = Readable | ReadStream; 6 | export type Package = { 7 | name: string; 8 | version: string; 9 | }; 10 | 11 | export type Tree = { 12 | name: string; 13 | children?: Trees; 14 | hint?: string; 15 | hidden?: boolean; 16 | color?: string; 17 | }; 18 | 19 | export type Trees = Array; 20 | 21 | export type ReporterSpinner = { 22 | tick: (name: string) => void; 23 | end: () => void; 24 | }; 25 | 26 | export type ReporterSpinnerSet = { 27 | spinners: Array; 28 | end: () => void; 29 | }; 30 | 31 | export type ReporterSetSpinner = { 32 | clear: () => void; 33 | setPrefix: (current: number, prefix: string) => void; 34 | tick: (msg: string) => void; 35 | end: () => void; 36 | }; 37 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import {Reporter} from './reporters/index.js'; 2 | import Config from './config.js'; 3 | 4 | export type CLIFunction = (config: Config, reporter: Reporter, flags: Object, args: Array) => CLIFunctionReturn; 5 | 6 | type _CLIFunctionReturn = boolean; 7 | export type CLIFunctionReturn = _CLIFunctionReturn | Promise<_CLIFunctionReturn>; 8 | 9 | // person object, the exploded version of a `maintainers`/`authors` field 10 | export type PersonObject = { 11 | email?: string; 12 | name?: string; 13 | url?: string; 14 | }; 15 | 16 | // `dependencies` field in package info 17 | type Dependencies = { 18 | [key: string]: string; 19 | }; 20 | 21 | // package.json 22 | export type Manifest = { 23 | name: string; 24 | version: string; 25 | description?: string; 26 | keywords?: string[]; 27 | sideEffects?: boolean; 28 | 29 | private?: boolean; 30 | 31 | distributions?: any; 32 | 33 | author?: { 34 | name?: string; 35 | email?: string; 36 | url?: string; 37 | }; 38 | 39 | homepage?: string; 40 | flat?: boolean; 41 | license?: string; 42 | licenseText?: string; 43 | noticeText?: string; 44 | 45 | readme?: string; 46 | readmeFilename?: string; 47 | 48 | repository?: { 49 | type: 'git'; 50 | url: string; 51 | }; 52 | 53 | bugs?: { 54 | url: string; 55 | }; 56 | 57 | // the package reference that 58 | dist?: { 59 | tarball: string; 60 | shasum: string; 61 | }; 62 | 63 | directories?: { 64 | man: string; 65 | bin: string; 66 | }; 67 | 68 | man?: Array; 69 | 70 | bin?: { 71 | [name: string]: string; 72 | }; 73 | 74 | scripts?: { 75 | [name: string]: string; 76 | }; 77 | 78 | engines?: { 79 | [engineName: string]: string; 80 | }; 81 | 82 | os?: Array; 83 | cpu?: Array; 84 | 85 | dependencies?: Dependencies; 86 | devDependencies?: Dependencies; 87 | peerDependencies?: Dependencies; 88 | optionalDependencies?: Dependencies; 89 | 90 | bundleDependencies?: Array; 91 | bundledDependencies?: Array; 92 | 93 | installConfig?: { 94 | pnp?: boolean; 95 | }; 96 | 97 | deprecated?: string; 98 | files?: Array; 99 | main?: string; 100 | 101 | // This flag is true when we add a new package with `pika add `. 102 | // We need to preserve the flag because we print a list of new packages in 103 | // the end of the add command 104 | fresh?: boolean; 105 | 106 | prebuiltVariants?: {[filename: string]: string}; 107 | }; 108 | 109 | // Used by outdated and upgrade-interactive 110 | export type Dependency = { 111 | name: string; 112 | current: string; 113 | wanted: string; 114 | latest: string; 115 | url: string; 116 | hint?: string; 117 | range: string; 118 | upgradeTo: string; 119 | workspaceName: string; 120 | workspaceLoc: string; 121 | }; 122 | -------------------------------------------------------------------------------- /src/util/babel-plugin-import-rewrite.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | import * as nodeFs from 'fs'; 3 | import * as nodePath from 'path'; 4 | import * as url from 'url'; 5 | import chalk from 'chalk'; 6 | import {validateDynamicImportArguments} from './babel-validate-specifier.js'; 7 | 8 | const BareIdentifierFormat = /^((?:@[^\/]+\/)?[^\/]+)(\/.*)?$/; 9 | 10 | function log(symbol: string, fileName: string, errors: string | Array) { 11 | if (!Array.isArray(errors)) { 12 | errors = [errors]; 13 | } 14 | console.log(`${symbol} `, chalk.bold(fileName)); 15 | for (const error of errors) { 16 | console.log(` ${chalk.dim('≫')} ${error}`); 17 | } 18 | } 19 | 20 | export default function transform({template, types: t}): any { 21 | function rewriteImport(specifier, {opts, file}) { 22 | const {deps, addExtensions} = opts; 23 | 24 | try { 25 | url.parse(specifier); 26 | } catch (err) { 27 | return; 28 | } 29 | // URL w/o protocol 30 | if (specifier.substr(0, 2) === '//') { 31 | return; // Leave it alone 32 | } 33 | 34 | // Local path 35 | if (['.', '/'].indexOf(specifier.charAt(0)) >= 0) { 36 | if (addExtensions) { 37 | const extname = nodePath.extname(specifier); 38 | if (extname === '.js') { 39 | return; 40 | } 41 | if (extname) { 42 | console.warn('Unexpected file extension:', specifier); 43 | return; 44 | } 45 | const resolvedPath = nodePath.resolve(nodePath.dirname(file.opts.filename), specifier); 46 | try { 47 | const stat = nodeFs.lstatSync(resolvedPath); 48 | if (stat.isDirectory()) { 49 | return specifier + '/index'; 50 | } 51 | } catch (err) { 52 | // do nothing 53 | } 54 | return specifier + '.js'; 55 | } 56 | return; 57 | } 58 | 59 | // A 'bare' identifier 60 | const match = BareIdentifierFormat.exec(specifier); 61 | if (deps && match) { 62 | const packageName = match[1]; 63 | // const file = match[2] || ''; 64 | return deps[packageName]; 65 | } 66 | } 67 | 68 | return { 69 | visitor: { 70 | 'ImportDeclaration|ExportNamedDeclaration|ExportAllDeclaration'(path, {opts, file}) { 71 | if (!path.node.source) { 72 | return; 73 | } 74 | const rewrittenSpecifier = rewriteImport(path.node.source.value, {opts, file}); 75 | if (rewrittenSpecifier) { 76 | path.node.source.value = rewrittenSpecifier; 77 | } 78 | }, 79 | Import(path, {opts, file}) { 80 | const errors = validateDynamicImportArguments(path); 81 | if (errors.size > 0) { 82 | return; 83 | } 84 | 85 | const [importPath] = path.parent.arguments; 86 | const rewrittenSpecifier = rewriteImport(importPath.value, {opts, file}); 87 | if (rewrittenSpecifier) { 88 | importPath.value = rewrittenSpecifier; 89 | } 90 | }, 91 | }, 92 | }; 93 | } 94 | -------------------------------------------------------------------------------- /src/util/babel-validate-specifier.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | import chalk from 'chalk'; 3 | 4 | function getLineCol(node: any): string { 5 | const loc = node.loc.start; 6 | return chalk.dim(`[${loc.line}:${loc.column}]`); 7 | } 8 | 9 | export function validateDynamicImportArguments(path): Set { 10 | if (path.parent.arguments.length !== 1) { 11 | return new Set([ 12 | `${getLineCol(path.node)} "\`import()\` only accepts 1 argument, but got ${path.parent.arguments.length}`, 13 | ]); 14 | } 15 | const [argNode] = path.parent.arguments; 16 | if (argNode.type !== 'StringLiteral') { 17 | return new Set([ 18 | `${getLineCol( 19 | path.node, 20 | )} Pika expects strings as \`import()\` arguments. Treating this as an absolute file path.`, 21 | ]); 22 | } 23 | return new Set(); 24 | } 25 | -------------------------------------------------------------------------------- /src/util/blocking-queue.ts: -------------------------------------------------------------------------------- 1 | import map from './map.js'; 2 | 3 | export default class BlockingQueue { 4 | constructor(alias: string, maxConcurrency: number = Infinity) { 5 | this.concurrencyQueue = []; 6 | this.maxConcurrency = maxConcurrency; 7 | this.runningCount = 0; 8 | this.warnedStuck = false; 9 | this.alias = alias; 10 | this.first = true; 11 | 12 | this.running = map() || {}; 13 | this.queue = map() || {}; 14 | 15 | this.stuckTick = this.stuckTick.bind(this); 16 | } 17 | 18 | concurrencyQueue: Array<() => void>; 19 | warnedStuck: boolean; 20 | maxConcurrency: number; 21 | runningCount: number; 22 | stuckTimer?: NodeJS.Timeout; 23 | alias: string; 24 | first: boolean; 25 | 26 | queue: { 27 | [key: string]: Array<{ 28 | factory: () => Promise; 29 | resolve: (val: any) => void; 30 | reject: Function; 31 | }>; 32 | }; 33 | 34 | running: { 35 | [key: string]: boolean; 36 | }; 37 | 38 | stillActive() { 39 | if (this.stuckTimer) { 40 | clearTimeout(this.stuckTimer); 41 | } 42 | 43 | this.stuckTimer = setTimeout(this.stuckTick, 5000); 44 | 45 | // We need to check the existence of unref because of https://github.com/facebook/jest/issues/4559 46 | // $FlowFixMe: Node's setInterval returns a Timeout, not a Number 47 | this.stuckTimer.unref && this.stuckTimer.unref(); 48 | } 49 | 50 | stuckTick() { 51 | if (this.runningCount === 1) { 52 | this.warnedStuck = true; 53 | console.log( 54 | `The ${JSON.stringify(this.alias)} blocking queue may be stuck. 5 seconds ` + 55 | `without any activity with 1 worker: ${Object.keys(this.running)[0]}`, 56 | ); 57 | } 58 | } 59 | 60 | push(key: string, factory: () => Promise): Promise { 61 | if (this.first) { 62 | this.first = false; 63 | } else { 64 | this.stillActive(); 65 | } 66 | 67 | return new Promise((resolve, reject) => { 68 | // we're already running so push ourselves to the queue 69 | const queue = (this.queue[key] = this.queue[key] || []); 70 | queue.push({factory, resolve, reject}); 71 | 72 | if (!this.running[key]) { 73 | this.shift(key); 74 | } 75 | }); 76 | } 77 | 78 | shift(key: string) { 79 | if (this.running[key]) { 80 | delete this.running[key]; 81 | this.runningCount--; 82 | 83 | if (this.stuckTimer) { 84 | clearTimeout(this.stuckTimer); 85 | this.stuckTimer = null; 86 | } 87 | 88 | if (this.warnedStuck) { 89 | this.warnedStuck = false; 90 | console.log(`${JSON.stringify(this.alias)} blocking queue finally resolved. Nothing to worry about.`); 91 | } 92 | } 93 | 94 | const queue = this.queue[key]; 95 | if (!queue) { 96 | return; 97 | } 98 | 99 | const {resolve, reject, factory} = queue.shift(); 100 | if (!queue.length) { 101 | delete this.queue[key]; 102 | } 103 | 104 | const next = () => { 105 | this.shift(key); 106 | this.shiftConcurrencyQueue(); 107 | }; 108 | 109 | const run = () => { 110 | this.running[key] = true; 111 | this.runningCount++; 112 | 113 | factory() 114 | .then(function (val): null { 115 | resolve(val); 116 | next(); 117 | return null; 118 | }) 119 | .catch(function (err) { 120 | reject(err); 121 | next(); 122 | }); 123 | }; 124 | 125 | this.maybePushConcurrencyQueue(run); 126 | } 127 | 128 | maybePushConcurrencyQueue(run: () => void) { 129 | if (this.runningCount < this.maxConcurrency) { 130 | run(); 131 | } else { 132 | this.concurrencyQueue.push(run); 133 | } 134 | } 135 | 136 | shiftConcurrencyQueue() { 137 | if (this.runningCount < this.maxConcurrency) { 138 | const fn = this.concurrencyQueue.shift(); 139 | if (fn) { 140 | fn(); 141 | } 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/util/child.ts: -------------------------------------------------------------------------------- 1 | /* global child_process$spawnOpts */ 2 | 3 | import * as constants from '../constants.js'; 4 | import BlockingQueue from './blocking-queue.js'; 5 | import {ProcessSpawnError, ProcessTermError} from '../errors.js'; 6 | import {promisify} from './promise.js'; 7 | import {exec as _exec, spawn as _spawn, ChildProcess, SpawnOptions} from 'child_process'; 8 | 9 | export const queue = new BlockingQueue('child', constants.CHILD_CONCURRENCY); 10 | 11 | // TODO: this uid check is kinda whack 12 | let uid = 0; 13 | 14 | export const exec = promisify(_exec); 15 | 16 | const spawnedProcesses = {}; 17 | 18 | export function forwardSignalToSpawnedProcesses(signal: string) { 19 | for (const key of Object.keys(spawnedProcesses)) { 20 | spawnedProcesses[key].kill(signal); 21 | } 22 | } 23 | 24 | type ProcessFn = ( 25 | proc: ChildProcess, 26 | update: (chunk: string) => void, 27 | reject: (err: any) => void, 28 | done: () => void, 29 | ) => void; 30 | 31 | export function spawn( 32 | program: string, 33 | args: Array, 34 | opts: SpawnOptions & {detached?: boolean; process?: ProcessFn} = {}, 35 | onData?: (chunk: Buffer | string) => void, 36 | ): Promise { 37 | const key = opts.cwd || String(++uid); 38 | return queue.push( 39 | key, 40 | (): Promise => 41 | new Promise((resolve, reject) => { 42 | const proc = _spawn(program, args, opts); 43 | spawnedProcesses[key] = proc; 44 | 45 | let processingDone = false; 46 | let processClosed = false; 47 | let err = null; 48 | 49 | let stdout = ''; 50 | 51 | proc.on('error', (err: any) => { 52 | if (err.code === 'ENOENT') { 53 | reject(new ProcessSpawnError(`Couldn't find the binary ${program}`, err.code, program)); 54 | } else { 55 | reject(err); 56 | } 57 | }); 58 | 59 | function updateStdout(chunk: string) { 60 | stdout += chunk; 61 | if (onData) { 62 | onData(chunk); 63 | } 64 | } 65 | 66 | function finish() { 67 | delete spawnedProcesses[key]; 68 | if (err) { 69 | reject(err); 70 | } else { 71 | resolve(stdout.trim()); 72 | } 73 | } 74 | 75 | if (typeof opts.process === 'function') { 76 | opts.process(proc, updateStdout, reject, function () { 77 | if (processClosed) { 78 | finish(); 79 | } else { 80 | processingDone = true; 81 | } 82 | }); 83 | } else { 84 | if (proc.stderr) { 85 | proc.stderr.on('data', updateStdout); 86 | } 87 | 88 | if (proc.stdout) { 89 | proc.stdout.on('data', updateStdout); 90 | } 91 | 92 | processingDone = true; 93 | } 94 | 95 | proc.on('close', (code: number, signal: string) => { 96 | if (signal || code >= 1) { 97 | err = new ProcessTermError( 98 | [ 99 | 'Command failed.', 100 | signal ? `Exit signal: ${signal}` : `Exit code: ${code}`, 101 | `Command: ${program}`, 102 | `Arguments: ${args.join(' ')}`, 103 | `Directory: ${opts.cwd || process.cwd()}`, 104 | `Output:\n${stdout.trim()}`, 105 | ].join('\n'), 106 | ); 107 | err.EXIT_SIGNAL = signal; 108 | err.EXIT_CODE = code; 109 | } 110 | 111 | if (processingDone || err) { 112 | finish(); 113 | } else { 114 | processClosed = true; 115 | } 116 | }); 117 | }), 118 | ); 119 | } 120 | -------------------------------------------------------------------------------- /src/util/conversion.ts: -------------------------------------------------------------------------------- 1 | const FALSY_STRINGS = new Set(['0', 'false']); 2 | 3 | export function boolify(val: string | number | boolean): boolean { 4 | return !FALSY_STRINGS.has(val.toString().toLowerCase()); 5 | } 6 | 7 | export function boolifyWithDefault(val: string | number | boolean, defaultResult: boolean): boolean { 8 | return val === '' || val === null || val === undefined ? defaultResult : boolify(val); 9 | } 10 | -------------------------------------------------------------------------------- /src/util/fix-cmd-win-slashes.ts: -------------------------------------------------------------------------------- 1 | export function fixCmdWinSlashes(cmd: string): string { 2 | function findQuotes(quoteSymbol: string): {from: number; to: number}[] { 3 | const quotes = []; 4 | const addQuote = (_, index) => { 5 | quotes.push({from: index, to: index + _.length}); 6 | return _; 7 | }; 8 | const regEx = new RegExp(quoteSymbol + '.*' + quoteSymbol); 9 | cmd.replace(regEx, addQuote); 10 | return quotes; 11 | } 12 | const quotes = findQuotes('"').concat(findQuotes("'")); 13 | 14 | function isInsideQuotes(index: number): boolean { 15 | return quotes.reduce((result, quote) => { 16 | return result || (quote.from <= index && index <= quote.to); 17 | }, false); 18 | } 19 | 20 | const cmdPrePattern = '((?:^|&&|&|\\|\\||\\|)\\s*)'; 21 | const cmdPattern = '(".*?"|\'.*?\'|\\S*)'; 22 | const regExp = new RegExp(`${cmdPrePattern}${cmdPattern}`, 'g'); 23 | return cmd.replace(regExp, (whole, pre, cmd, index) => { 24 | if ((pre[0] === '&' || pre[0] === '|') && isInsideQuotes(index)) { 25 | return whole; 26 | } 27 | return pre + cmd.replace(/\//g, '\\'); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /src/util/fs-normalized.ts: -------------------------------------------------------------------------------- 1 | // 2 | 3 | // // This module serves as a wrapper for file operations that are inconsistant across node and OS versions. 4 | 5 | // import fs from 'fs'; 6 | // import {promisify} from './promise.js'; 7 | 8 | // import {constants} from './fs.js'; 9 | 10 | // export type CopyFileAction = { 11 | // src: string, 12 | // dest: string, 13 | // atime: Date, 14 | // mtime: Date, 15 | // mode: number, 16 | // }; 17 | 18 | // let disableTimestampCorrection: boolean; // OS dependent. will be detected on first file copy. 19 | 20 | // const readFileBuffer = promisify(fs.readFile); 21 | // const close: (fd: number) => Promise = promisify(fs.close); 22 | // const lstat: (path: string) => Promise = promisify(fs.lstat); 23 | // const open: (path: string, flags: string | number, mode: number) => Promise = promisify(fs.open); 24 | // const futimes: (fd: number, atime: number, mtime: number) => Promise = promisify(fs.futimes); 25 | 26 | // const write: ( 27 | // fd: number, 28 | // buffer: Buffer, 29 | // offset?: number, 30 | // length?: number, 31 | // position?: number, 32 | // ) => Promise = promisify(fs.write); 33 | 34 | // /** 35 | // * Unlinks the destination to force a recreation. This is needed on case-insensitive file systems 36 | // * to force the correct naming when the filename has changed only in character-casing. (Jest -> jest). 37 | // */ 38 | // export const copyFile = async function(data: CopyFileAction, cleanup: () => any): Promise { 39 | // // $FlowFixMe: Flow doesn't currently support COPYFILE_FICLONE 40 | // const ficloneFlag = (constants as any).COPYFILE_FICLONE || 0; 41 | // try { 42 | // await unlink(data.dest); 43 | // await copyFilePoly(data.src, data.dest, ficloneFlag, data); 44 | // } finally { 45 | // if (cleanup) { 46 | // cleanup(); 47 | // } 48 | // } 49 | // }; 50 | 51 | // // Node 8.5.0 introduced `fs.copyFile` which is much faster, so use that when available. 52 | // // Otherwise we fall back to reading and writing files as buffers. 53 | // const copyFilePoly: (src: string, dest: string, flags: number, data: CopyFileAction) => Promise = ( 54 | // src, 55 | // dest, 56 | // flags, 57 | // data, 58 | // ) => { 59 | // if (fs.copyFile) { 60 | // return new Promise((resolve, reject) => 61 | // fs.copyFile(src, dest, flags, err => { 62 | // if (err) { 63 | // reject(err); 64 | // } else { 65 | // fixTimes(undefined, dest, data).then(() => resolve()).catch(ex => reject(ex)); 66 | // } 67 | // }), 68 | // ); 69 | // } else { 70 | // return copyWithBuffer(src, dest, flags, data); 71 | // } 72 | // }; 73 | 74 | // const copyWithBuffer: (src: string, dest: string, flags: number, data: CopyFileAction) => Promise = async ( 75 | // src, 76 | // dest, 77 | // flags, 78 | // data, 79 | // ) => { 80 | // // Use open -> write -> futimes -> close sequence to avoid opening the file twice: 81 | // // one with writeFile and one with utimes 82 | // const fd = await open(dest, 'w', data.mode); 83 | // try { 84 | // const buffer = await readFileBuffer(src); 85 | // await write(fd, buffer, 0, buffer.length); 86 | // await fixTimes(fd, dest, data); 87 | // } finally { 88 | // await close(fd); 89 | // } 90 | // }; 91 | 92 | // // We want to preserve file timestamps when copying a file, since pika uses them to decide if a file has 93 | // // changed compared to the cache. 94 | // // There are some OS specific cases here: 95 | // // * On linux, fs.copyFile does not preserve timestamps, but does on OSX and Win. 96 | // // * On windows, you must open a file with write permissions to call `fs.futimes`. 97 | // // * On OSX you can open with read permissions and still call `fs.futimes`. 98 | // async function fixTimes(fd: number | undefined, dest: string, data: CopyFileAction): Promise { 99 | // const doOpen = fd === undefined; 100 | // let openfd: number = fd ? fd : -1; 101 | 102 | // if (disableTimestampCorrection === undefined) { 103 | // // if timestamps match already, no correction is needed. 104 | // // the need to correct timestamps varies based on OS and node versions. 105 | // const destStat = await lstat(dest); 106 | // disableTimestampCorrection = fileDatesEqual(destStat.mtime, data.mtime); 107 | // } 108 | 109 | // if (disableTimestampCorrection) { 110 | // return; 111 | // } 112 | 113 | // if (doOpen) { 114 | // try { 115 | // openfd = await open(dest, 'a', data.mode); 116 | // } catch (er) { 117 | // // file is likely read-only 118 | // try { 119 | // openfd = await open(dest, 'r', data.mode); 120 | // } catch (err) { 121 | // // We can't even open this file for reading. 122 | // return; 123 | // } 124 | // } 125 | // } 126 | 127 | // try { 128 | // if (openfd) { 129 | // await futimes(openfd, data.atime, data.mtime); 130 | // } 131 | // } catch (er) { 132 | // // If `futimes` throws an exception, we probably have a case of a read-only file on Windows. 133 | // // In this case we can just return. The incorrect timestamp will just cause that file to be recopied 134 | // // on subsequent installs, which will effect pika performance but not break anything. 135 | // } finally { 136 | // if (doOpen && openfd) { 137 | // await close(openfd); 138 | // } 139 | // } 140 | // } 141 | 142 | // // Compare file timestamps. 143 | // // Some versions of Node on windows zero the milliseconds when utime is used. 144 | // export const fileDatesEqual = (a: Date, b: Date) => { 145 | // const aTime = a.getTime(); 146 | // const bTime = b.getTime(); 147 | 148 | // if (process.platform !== 'win32') { 149 | // return aTime === bTime; 150 | // } 151 | 152 | // // See https://github.com/nodejs/node/pull/12607 153 | // // Submillisecond times from stat and utimes are truncated on Windows, 154 | // // causing a file with mtime 8.0079998 and 8.0081144 to become 8.007 and 8.008 155 | // // and making it impossible to update these files to their correct timestamps. 156 | // if (Math.abs(aTime - bTime) <= 1) { 157 | // return true; 158 | // } 159 | 160 | // const aTimeSec = Math.floor(aTime / 1000); 161 | // const bTimeSec = Math.floor(bTime / 1000); 162 | 163 | // // See https://github.com/nodejs/node/issues/2069 164 | // // Some versions of Node on windows zero the milliseconds when utime is used 165 | // // So if any of the time has a milliseconds part of zero we suspect that the 166 | // // bug is present and compare only seconds. 167 | // if (aTime - aTimeSec * 1000 === 0 || bTime - bTimeSec * 1000 === 0) { 168 | // return aTimeSec === bTimeSec; 169 | // } 170 | 171 | // return aTime === bTime; 172 | // }; 173 | -------------------------------------------------------------------------------- /src/util/map.ts: -------------------------------------------------------------------------------- 1 | export default function nullify(obj?: T): T { 2 | if (Array.isArray(obj)) { 3 | for (const item of obj) { 4 | nullify(item); 5 | } 6 | } else if ((obj !== null && typeof obj === 'object') || typeof obj === 'function') { 7 | Object.setPrototypeOf(obj, null); 8 | 9 | // for..in can only be applied to 'object', not 'function' 10 | if (typeof obj === 'object') { 11 | for (const key in obj) { 12 | nullify(obj[key]); 13 | } 14 | } 15 | } 16 | 17 | return obj; 18 | } 19 | -------------------------------------------------------------------------------- /src/util/misc.ts: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export function sortAlpha(a: string, b: string): number { 4 | // sort alphabetically in a deterministic way 5 | const shortLen = Math.min(a.length, b.length); 6 | for (let i = 0; i < shortLen; i++) { 7 | const aChar = a.charCodeAt(i); 8 | const bChar = b.charCodeAt(i); 9 | if (aChar !== bChar) { 10 | return aChar - bChar; 11 | } 12 | } 13 | return a.length - b.length; 14 | } 15 | 16 | export function sortOptionsByFlags(a: any, b: any): number { 17 | const aOpt = a.flags.replace(/-/g, ''); 18 | const bOpt = b.flags.replace(/-/g, ''); 19 | return sortAlpha(aOpt, bOpt); 20 | } 21 | 22 | export function entries(obj: {[key: string]: T}): Array<[string, T]> { 23 | const entries = []; 24 | if (obj) { 25 | for (const key in obj) { 26 | entries.push([key, obj[key]]); 27 | } 28 | } 29 | return entries; 30 | } 31 | 32 | export function removePrefix(pattern: string, prefix: string): string { 33 | if (pattern.startsWith(prefix)) { 34 | pattern = pattern.slice(prefix.length); 35 | } 36 | 37 | return pattern; 38 | } 39 | 40 | export function removeSuffix(pattern: string, suffix: string): string { 41 | if (pattern.endsWith(suffix)) { 42 | return pattern.slice(0, -suffix.length); 43 | } 44 | 45 | return pattern; 46 | } 47 | 48 | export function addSuffix(pattern: string, suffix: string): string { 49 | if (!pattern.endsWith(suffix)) { 50 | return pattern + suffix; 51 | } 52 | 53 | return pattern; 54 | } 55 | 56 | export function hyphenate(str: string): string { 57 | return str.replace(/[A-Z]/g, (match) => { 58 | return '-' + match.charAt(0).toLowerCase(); 59 | }); 60 | } 61 | 62 | export function compareSortedArrays(array1: Array, array2: Array): boolean { 63 | if (array1.length !== array2.length) { 64 | return false; 65 | } 66 | for (let i = 0, len = array1.length; i < len; i++) { 67 | if (array1[i] !== array2[i]) { 68 | return false; 69 | } 70 | } 71 | return true; 72 | } 73 | 74 | export function sleep(ms: number): Promise { 75 | return new Promise((resolve) => { 76 | setTimeout(resolve, ms); 77 | }); 78 | } 79 | -------------------------------------------------------------------------------- /src/util/normalize-manifest/for-publish.ts: -------------------------------------------------------------------------------- 1 | import {Manifest} from '../../types'; 2 | import Config from '../../config'; 3 | 4 | export async function generatePublishManifest( 5 | manifest: any, 6 | config: Config, 7 | _dists?: Array<[Function, any]>, 8 | ): Promise { 9 | const { 10 | name, 11 | version, 12 | description, 13 | keywords, 14 | homepage, 15 | bugs, 16 | bin, 17 | license, 18 | authors, 19 | contributors, 20 | man, 21 | sideEffects, 22 | repository, 23 | dependencies, 24 | peerDependencies, 25 | devDependencies, 26 | bundledDependencies, 27 | optionalDependencies, 28 | engines, 29 | enginesStrict, 30 | private: priv, 31 | publishConfig, 32 | } = manifest; 33 | 34 | const newManifest = { 35 | name, 36 | description, 37 | version, 38 | license, 39 | bin, 40 | files: ['dist-*/', 'bin/'], 41 | pika: true, 42 | sideEffects: sideEffects || false, 43 | keywords, 44 | homepage, 45 | bugs, 46 | authors, 47 | contributors, 48 | man, 49 | repository, 50 | dependencies: dependencies || {}, 51 | peerDependencies, 52 | devDependencies, 53 | bundledDependencies, 54 | optionalDependencies, 55 | engines, 56 | enginesStrict, 57 | private: priv, 58 | publishConfig, 59 | }; 60 | 61 | const dists = _dists || (await config.getDistributions()); 62 | for (const [runner, options] of dists) { 63 | if (runner.manifest) { 64 | await runner.manifest(newManifest, { 65 | cwd: config.cwd, 66 | isFull: true, 67 | manifest, 68 | options, 69 | }); 70 | } 71 | } 72 | 73 | newManifest.pika = true; 74 | return newManifest; 75 | } 76 | 77 | export function generatePrettyManifest(manifest) { 78 | return JSON.stringify( 79 | { 80 | ...manifest, 81 | dependencies: Object.keys(manifest.dependencies).length === 0 ? {} : '{ ... }', 82 | }, 83 | null, 84 | 2, 85 | ); 86 | } 87 | -------------------------------------------------------------------------------- /src/util/normalize-manifest/index.ts: -------------------------------------------------------------------------------- 1 | import {Manifest} from '../../types.js'; 2 | import Config from '../../config.js'; 3 | import validate from './validate.js'; 4 | import fix from './fix.js'; 5 | 6 | import * as path from 'path'; 7 | 8 | export default (async function (info: any, moduleLoc: string, config: Config, isRoot: boolean): Promise { 9 | // Append dependencies 10 | // if (depInfo) { 11 | // info.dependencies = depInfo.main; 12 | // info.devDependencies = depInfo.dev; 13 | // } 14 | // create human readable name 15 | const {name, version} = info; 16 | let human: string | undefined; 17 | if (typeof name === 'string') { 18 | human = name; 19 | } 20 | if (human && typeof version === 'string' && version) { 21 | human += `@${version}`; 22 | } 23 | if (isRoot && info._loc) { 24 | human = path.relative(config.cwd, info._loc); 25 | } 26 | 27 | function warn(msg: string) { 28 | if (human) { 29 | msg = `${human}: ${msg}`; 30 | } 31 | config.reporter.warn(msg); 32 | } 33 | 34 | await fix(info, moduleLoc, config.reporter, warn); 35 | 36 | try { 37 | validate(info, isRoot, config.reporter, warn); 38 | } catch (err) { 39 | if (human) { 40 | err.message = `${human}: ${err.message}`; 41 | } 42 | throw err; 43 | } 44 | 45 | return info; 46 | }); 47 | -------------------------------------------------------------------------------- /src/util/normalize-manifest/infer-license.ts: -------------------------------------------------------------------------------- 1 | import LICENSES from './licenses.js'; 2 | 3 | function clean(str: string): string { 4 | return str 5 | .replace(/[^A-Za-z\s]/g, ' ') 6 | .replace(/[\s]+/g, ' ') 7 | .trim() 8 | .toLowerCase(); 9 | } 10 | 11 | const REGEXES: {[key: string]: Array} = { 12 | Apache: [/Apache License\b/], 13 | BSD: [/BSD\b/], 14 | ISC: [/The ISC License/, /ISC\b/], 15 | MIT: [/MIT\b/], 16 | Unlicense: [/http:\/\/unlicense.org\//], 17 | WTFPL: [/DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE/, /WTFPL\b/], 18 | }; 19 | 20 | export default function inferLicense(license: string): string | null { 21 | // check if we have any explicit licenses 22 | const cleanLicense = clean(license); 23 | for (const licenseName in LICENSES) { 24 | const testLicense = LICENSES[licenseName]; 25 | if (cleanLicense.search(testLicense) >= 0) { 26 | return licenseName; 27 | } 28 | } 29 | 30 | // infer based on some keywords 31 | for (const licenseName in REGEXES) { 32 | for (const regex of REGEXES[licenseName]) { 33 | if (license.search(regex) >= 0) { 34 | return `${licenseName}*`; 35 | } 36 | } 37 | } 38 | 39 | return null; 40 | } 41 | -------------------------------------------------------------------------------- /src/util/normalize-manifest/typos.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | autohr: 'author', 3 | autor: 'author', 4 | contributers: 'contributors', 5 | depdenencies: 'dependencies', 6 | dependancies: 'dependencies', 7 | dependecies: 'dependencies', 8 | depends: 'dependencies', 9 | 'dev-dependencies': 'devDependencies', 10 | devDependences: 'devDependencies', 11 | devDepenencies: 'devDependencies', 12 | devEependencies: 'devDependencies', 13 | devdependencies: 'devDependencies', 14 | hampage: 'homepage', 15 | hompage: 'homepage', 16 | prefereGlobal: 'preferGlobal', 17 | publicationConfig: 'publishConfig', 18 | repo: 'repository', 19 | repostitory: 'repository', 20 | script: 'scripts', 21 | }; 22 | -------------------------------------------------------------------------------- /src/util/normalize-manifest/util.ts: -------------------------------------------------------------------------------- 1 | import {PersonObject} from '../../types.js'; 2 | 3 | import validateLicense from 'validate-npm-package-license'; 4 | 5 | export function isValidLicense(license: string): boolean { 6 | return !!license && validateLicense(license).validForNewPackages; 7 | } 8 | 9 | export function stringifyPerson(person: any): any { 10 | if (!person || typeof person !== 'object') { 11 | return person; 12 | } 13 | 14 | const parts = []; 15 | if (person.name) { 16 | parts.push(person.name); 17 | } 18 | 19 | const email = person.email || person.mail; 20 | if (typeof email === 'string') { 21 | parts.push(`<${email}>`); 22 | } 23 | 24 | const url = person.url || person.web; 25 | if (typeof url === 'string') { 26 | parts.push(`(${url})`); 27 | } 28 | 29 | return parts.join(' '); 30 | } 31 | 32 | export function parsePerson(person: any): PersonObject { 33 | if (typeof person !== 'string') { 34 | return person; 35 | } 36 | 37 | // format: name (url) 38 | const obj: PersonObject = {}; 39 | 40 | let name = person.match(/^([^\(<]+)/); 41 | if (name && name[0].trim()) { 42 | obj.name = name[0].trim(); 43 | } 44 | 45 | const email = person.match(/<([^>]+)>/); 46 | if (email) { 47 | obj.email = email[1]; 48 | } 49 | 50 | const url = person.match(/\(([^\)]+)\)/); 51 | if (url) { 52 | obj.url = url[1]; 53 | } 54 | 55 | return obj; 56 | } 57 | 58 | export function normalizePerson(person: any): any | PersonObject { 59 | return parsePerson(stringifyPerson(person)); 60 | } 61 | 62 | export function extractDescription(readme: any): string { 63 | if (typeof readme !== 'string' || readme === '') { 64 | return undefined; 65 | } 66 | 67 | // split into lines 68 | const lines = readme 69 | .trim() 70 | .split('\n') 71 | .map((line): string => line.trim()); 72 | 73 | // find the start of the first paragraph, ignore headings 74 | let start = 0; 75 | for (; start < lines.length; start++) { 76 | const line = lines[start]; 77 | if (line && line.match(/^(#|$)/)) { 78 | // line isn't empty and isn't a heading so this is the start of a paragraph 79 | start++; 80 | break; 81 | } 82 | } 83 | 84 | // skip newlines from the header to the first line 85 | while (start < lines.length && !lines[start]) { 86 | start++; 87 | } 88 | 89 | // continue to the first non empty line 90 | let end = start; 91 | while (end < lines.length && lines[end]) { 92 | end++; 93 | } 94 | 95 | return lines.slice(start, end).join(' '); 96 | } 97 | -------------------------------------------------------------------------------- /src/util/normalize-manifest/validate.ts: -------------------------------------------------------------------------------- 1 | import {Reporter} from '../../reporters/index.js'; 2 | import {MessageError} from '@pika/types'; 3 | import typos from './typos.js'; 4 | 5 | import isBuiltinModule from 'is-builtin-module'; 6 | 7 | const strings = ['name', 'version']; 8 | 9 | const dependencyKeys = [ 10 | // npm registry will include optionalDependencies in dependencies and we'll want to dedupe them from the 11 | // other fields first 12 | 'optionalDependencies', 13 | 14 | // it's seemingly common to include a dependency in dependencies and devDependencies of the same name but 15 | // different ranges, this can cause a lot of issues with our determinism and the behaviour of npm is 16 | // currently unspecified. 17 | 'dependencies', 18 | 19 | 'devDependencies', 20 | ]; 21 | 22 | function isValidName(name: string): boolean { 23 | return !name.match(/[\/@\s\+%:]/) && encodeURIComponent(name) === name; 24 | } 25 | 26 | function isValidScopedName(name: string): boolean { 27 | if (name[0] !== '@') { 28 | return false; 29 | } 30 | 31 | const parts = name.slice(1).split('/'); 32 | return parts.length === 2 && isValidName(parts[0]) && isValidName(parts[1]); 33 | } 34 | 35 | export function isValidPackageName(name: string): boolean { 36 | return isValidName(name) || isValidScopedName(name); 37 | } 38 | 39 | type WarnFunction = (msg: string) => void; 40 | 41 | export default function (info: any, isRoot: boolean, reporter: Reporter, warn: WarnFunction) { 42 | if (isRoot) { 43 | for (const key in typos) { 44 | if (key in info) { 45 | warn(reporter.lang('manifestPotentialTypo', key, typos[key])); 46 | } 47 | } 48 | } 49 | 50 | // validate name 51 | const {name} = info; 52 | if (typeof name === 'string') { 53 | if (isRoot && isBuiltinModule(name)) { 54 | warn(reporter.lang('manifestBuiltinModule', name)); 55 | } 56 | 57 | // cannot start with a dot 58 | if (name[0] === '.') { 59 | throw new MessageError(reporter.lang('manifestNameDot')); 60 | } 61 | 62 | // cannot contain the following characters 63 | if (!isValidPackageName(name)) { 64 | throw new MessageError(reporter.lang('manifestNameIllegalChars')); 65 | } 66 | 67 | // cannot equal node_modules or favicon.ico 68 | const lower = name.toLowerCase(); 69 | if (lower === 'node_modules' || lower === 'favicon.ico') { 70 | throw new MessageError(reporter.lang('manifestNameBlacklisted')); 71 | } 72 | } 73 | 74 | // Only care if you are trying to publish to npm. 75 | // // validate license 76 | // if (isRoot && !info.private) { 77 | // if (typeof info.license === 'string') { 78 | // const license = info.license.replace(/\*$/g, ''); 79 | // if (!isValidLicense(license)) { 80 | // warn(reporter.lang('manifestLicenseInvalid')); 81 | // } 82 | // } else { 83 | // warn(reporter.lang('manifestLicenseNone')); 84 | // } 85 | // } 86 | 87 | // validate strings 88 | for (const key of strings) { 89 | const val = info[key]; 90 | if (val && typeof val !== 'string') { 91 | throw new MessageError(reporter.lang('manifestStringExpected', key)); 92 | } 93 | } 94 | 95 | cleanDependencies(info, isRoot, reporter, warn); 96 | } 97 | 98 | export function cleanDependencies(info: Object, isRoot: boolean, reporter: Reporter, warn: WarnFunction) { 99 | // get dependency objects 100 | const depTypes = []; 101 | for (const type of dependencyKeys) { 102 | const deps = info[type]; 103 | if (!deps || typeof deps !== 'object') { 104 | continue; 105 | } 106 | depTypes.push([type, deps]); 107 | } 108 | 109 | // aggregate all non-trivial deps (not '' or '*') 110 | const nonTrivialDeps: Map = new Map(); 111 | for (const [type, deps] of depTypes) { 112 | for (const name of Object.keys(deps)) { 113 | const version = deps[name]; 114 | if (!nonTrivialDeps.has(name) && version && version !== '*') { 115 | nonTrivialDeps.set(name, {type, version}); 116 | } 117 | } 118 | } 119 | 120 | // overwrite first dep of package with non-trivial version, remove the rest 121 | const setDeps: Set = new Set(); 122 | for (const [type, deps] of depTypes) { 123 | for (const name of Object.keys(deps)) { 124 | let version = deps[name]; 125 | 126 | const dep = nonTrivialDeps.get(name); 127 | if (dep) { 128 | if (version && version !== '*' && version !== dep.version && isRoot) { 129 | // only throw a warning when at the root 130 | warn(reporter.lang('manifestDependencyCollision', dep.type, name, dep.version, type, version)); 131 | } 132 | version = dep.version; 133 | } 134 | 135 | if (setDeps.has(name)) { 136 | delete deps[name]; 137 | } else { 138 | deps[name] = version; 139 | setDeps.add(name); 140 | } 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/util/promise.ts: -------------------------------------------------------------------------------- 1 | export function wait(delay: number): Promise { 2 | return new Promise((resolve) => { 3 | setTimeout(resolve, delay); 4 | }); 5 | } 6 | 7 | export function promisify(fn: Function, firstData?: boolean): (...args: Array) => Promise { 8 | return function (...args): Promise { 9 | return new Promise(function (resolve, reject) { 10 | args.push(function (err, ...result) { 11 | let res = result; 12 | 13 | if (result.length <= 1) { 14 | res = result[0]; 15 | } 16 | 17 | if (firstData) { 18 | res = err; 19 | err = null; 20 | } 21 | 22 | if (err) { 23 | reject(err); 24 | } else { 25 | resolve(res); 26 | } 27 | }); 28 | 29 | fn.apply(null, args); 30 | }); 31 | }; 32 | } 33 | 34 | export function queue( 35 | arr: Array, 36 | promiseProducer: (result: U) => Promise, 37 | concurrency: number = Infinity, 38 | ): Promise> { 39 | concurrency = Math.min(concurrency, arr.length); 40 | 41 | // clone 42 | arr = arr.slice(); 43 | 44 | const results = []; 45 | let total = arr.length; 46 | if (!total) { 47 | return Promise.resolve(results); 48 | } 49 | 50 | return new Promise((resolve, reject) => { 51 | for (let i = 0; i < concurrency; i++) { 52 | next(); 53 | } 54 | 55 | function next() { 56 | const item = arr.shift(); 57 | const promise = promiseProducer(item); 58 | 59 | promise.then(function (result) { 60 | results.push(result); 61 | 62 | total--; 63 | if (total === 0) { 64 | resolve(results); 65 | } else { 66 | if (arr.length) { 67 | next(); 68 | } 69 | } 70 | }, reject); 71 | } 72 | }); 73 | } 74 | -------------------------------------------------------------------------------- /src/util/signal-handler.ts: -------------------------------------------------------------------------------- 1 | import {forwardSignalToSpawnedProcesses} from './child.js'; 2 | 3 | function forwardSignalAndExit(signal: string) { 4 | forwardSignalToSpawnedProcesses(signal); 5 | // We want to exit immediately here since `SIGTERM` means that 6 | // If we lose stdout messages due to abrupt exit, shoot the messenger? 7 | process.exit(1); // eslint-disable-line no-process-exit 8 | } 9 | 10 | export default function handleSignals() { 11 | process.on('SIGTERM', () => { 12 | forwardSignalAndExit('SIGTERM'); 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "esModuleInterop": true 7 | }, 8 | "include": ["src/**/*"], 9 | "exclude": ["node_modules"] 10 | } 11 | --------------------------------------------------------------------------------