├── .eslintignore ├── .eslintrc.cjs ├── .github ├── FUNDING.yml └── workflows │ ├── build.yml │ ├── deploy.yml │ └── pr.yml ├── .gitignore ├── .moon ├── tasks.yml ├── toolchain.yml └── workspace.yml ├── .npmignore ├── .nvmrc ├── .prettierignore ├── .prettierrc.js ├── .yarn └── releases │ └── yarn-4.1.0.cjs ├── .yarnrc.yml ├── LICENSE ├── README.md ├── lerna.json ├── package.json ├── packages ├── babel-plugin-cjs-esm-interop │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tests │ │ └── plugin.test.ts │ ├── tsconfig.json │ └── tsconfig.lib.json ├── babel-plugin-conditional-invariant │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tests │ │ ├── __snapshots__ │ │ │ └── plugin.test.ts.snap │ │ └── plugin.test.ts │ ├── tsconfig.json │ └── tsconfig.lib.json ├── babel-plugin-env-constants │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── types.ts │ ├── tests │ │ ├── __snapshots__ │ │ │ └── plugin.test.ts.snap │ │ └── plugin.test.ts │ ├── tsconfig.json │ └── tsconfig.lib.json └── packemon │ ├── .eslintrc.cjs │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── moon.yml │ ├── package.json │ ├── src │ ├── Artifact.ts │ ├── Config.ts │ ├── FileSystem.ts │ ├── Package.ts │ ├── PackageValidator.ts │ ├── Packemon.ts │ ├── babel.ts │ ├── babel │ │ ├── config.ts │ │ └── resolve.ts │ ├── bin.ts │ ├── commands │ │ ├── Base.ts │ │ ├── Build.ts │ │ ├── BuildWorkspace.ts │ │ ├── Clean.ts │ │ ├── Files.tsx │ │ ├── Init.tsx │ │ ├── Pack.ts │ │ ├── PackWorkspace.ts │ │ ├── Scaffold.tsx │ │ ├── Validate.ts │ │ └── Watch.ts │ ├── components │ │ ├── Files │ │ │ ├── List.tsx │ │ │ ├── Symbol.tsx │ │ │ ├── Tree.tsx │ │ │ ├── TreeContext.ts │ │ │ └── index.tsx │ │ ├── Init │ │ │ ├── PackageForm.tsx │ │ │ └── index.tsx │ │ └── Scaffold │ │ │ ├── TemplateSelect.tsx │ │ │ └── index.tsx │ ├── constants.ts │ ├── helpers │ │ ├── compat │ │ │ └── convertCjsTypes.ts │ │ ├── getVersion.ts │ │ ├── injectDefaultCondition.ts │ │ ├── loadModule.ts │ │ ├── loadTsconfigJson.ts │ │ ├── matchesPattern.ts │ │ ├── mergeExports.ts │ │ ├── removeSourcePath.ts │ │ ├── shouldKeepDynamicImport.ts │ │ ├── sortExportConditions.ts │ │ └── sortExports.ts │ ├── index.ts │ ├── rollup │ │ ├── config.ts │ │ └── plugins │ │ │ ├── addBinShebang.ts │ │ │ ├── addMjsWrapperForCjs.ts │ │ │ ├── copyAndRefAssets.ts │ │ │ ├── preserveDynamicImport.ts │ │ │ └── swc.ts │ ├── schemas.ts │ ├── swc │ │ └── config.ts │ └── types.ts │ ├── templates │ ├── base │ │ ├── .eslintrc.cjs │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── babel.config.js │ │ └── prettier.config.js │ ├── monorepo-package │ │ ├── README.md │ │ ├── package.json │ │ ├── tsconfig.esm.json │ │ └── tsconfig.json │ ├── monorepo │ │ ├── package.json │ │ ├── tsconfig.json │ │ └── tsconfig.options.json │ ├── package │ │ ├── package.json │ │ ├── src │ │ │ └── index.ts │ │ └── tests │ │ │ └── index.test.ts │ └── polyrepo │ │ ├── package.json │ │ ├── tsconfig.esm.json │ │ └── tsconfig.json │ ├── tests │ ├── Artifact.test.ts │ ├── Package.test.ts │ ├── PackageValidator.test.ts │ ├── Packemon.test.ts │ ├── __snapshots__ │ │ ├── Artifact.test.ts.snap │ │ ├── Package.test.ts.snap │ │ ├── assets.test.ts.snap │ │ └── outputs.test.ts.snap │ ├── assets.test.ts │ ├── babel │ │ ├── __snapshots__ │ │ │ └── config.test.ts.snap │ │ └── config.test.ts │ ├── configs.test.ts │ ├── examples │ │ ├── __snapshots__ │ │ │ ├── assetImports.test.ts.snap │ │ │ ├── asyncAwait.test.ts.snap │ │ │ ├── cjsEsmInterop.test.ts.snap │ │ │ ├── cjsMjsWrapper.test.ts.snap │ │ │ ├── dynamicImports.test.ts.snap │ │ │ ├── externals.test.ts.snap │ │ │ ├── generators.test.ts.snap │ │ │ ├── importAttributes.test.ts.snap │ │ │ ├── jsonImports.test.ts.snap │ │ │ ├── namespaces.test.ts.snap │ │ │ ├── newSyntax.test.ts.snap │ │ │ ├── nodePolyfills.test.ts.snap │ │ │ ├── react.test.ts.snap │ │ │ └── solid.test.ts.snap │ │ ├── assetImports.test.ts │ │ ├── asyncAwait.test.ts │ │ ├── cjsEsmInterop.test.ts │ │ ├── cjsMjsWrapper.test.ts │ │ ├── dynamicImports.test.ts │ │ ├── externals.test.ts │ │ ├── generators.test.ts │ │ ├── importAttributes.test.ts │ │ ├── jsonImports.test.ts │ │ ├── namespaces.test.ts │ │ ├── newSyntax.test.ts │ │ ├── nodePolyfills.test.ts │ │ ├── react.test.ts │ │ └── solid.test.ts │ ├── helpers.ts │ ├── helpers │ │ ├── loadModule.test.ts │ │ └── sortExportConditions.test.ts │ ├── outputs.test.ts │ ├── rollup │ │ ├── config.test.ts │ │ └── plugins │ │ │ ├── __fixtures__ │ │ │ ├── bin.ts │ │ │ └── src │ │ │ │ └── components │ │ │ │ ├── AnotherComponent │ │ │ │ └── anotherEntry.mjs │ │ │ │ └── MyComponent │ │ │ │ ├── MySubComponent │ │ │ │ ├── bar.mjs │ │ │ │ └── test.svg │ │ │ │ └── entry.mjs │ │ │ ├── addBinShebang.test.ts │ │ │ └── copyAndRefAssets.test.ts │ └── swc │ │ ├── __snapshots__ │ │ └── config.test.ts.snap │ │ └── config.test.ts │ ├── tsconfig.json │ ├── tsconfig.mjs.json │ └── types │ ├── ink-progress-bar.d.ts │ └── spdx-license-list.d.ts ├── scenarios ├── dual-exports-cjs-mjs │ ├── main.cjs │ ├── module.mjs │ └── package.json ├── dual-exports-conditions │ ├── browser-default.js │ ├── browser.js │ ├── default.js │ ├── node-default.cjs │ ├── node.cjs │ └── package.json ├── dual-exports-js │ ├── main.js │ ├── module.js │ └── package.json ├── dual-module-cjs-mjs │ ├── main.cjs │ ├── module.mjs │ └── package.json ├── dual-module-js │ ├── main.js │ ├── module.js │ └── package.json ├── esm-exports-js-module │ ├── index.js │ └── package.json ├── esm-exports-js │ ├── index.js │ └── package.json ├── esm-exports-mjs │ ├── index.mjs │ └── package.json ├── esm-module-js-module │ ├── index.js │ └── package.json ├── esm-module-js │ ├── index.js │ └── package.json ├── esm-module-mjs │ ├── index.mjs │ └── package.json ├── index.js ├── invalid-js-module-via-require │ ├── index.js │ └── package.json ├── invalid-mjs-via-require │ ├── index.mjs │ └── package.json ├── jest │ ├── __tests__ │ │ ├── cjs.test.js │ │ └── esm.test.mjs │ ├── babel.config.js │ ├── jest.config.js │ └── package.json ├── parcel │ └── package.json ├── snowpack │ ├── index.html │ └── package.json ├── valid-cjs-via-import │ ├── index.cjs │ └── package.json ├── vite │ ├── index.html │ └── package.json └── webpack │ └── package.json ├── scripts ├── buildPackages.mjs ├── deleteSwcTypes.mjs └── testSwc.mjs ├── tests ├── __fixtures__ │ ├── config-files-monorepo │ │ ├── package.json │ │ ├── packages │ │ │ ├── bar │ │ │ │ ├── .packemon.js │ │ │ │ └── package.json │ │ │ ├── baz │ │ │ │ ├── .packemon.ts │ │ │ │ └── package.json │ │ │ └── foo │ │ │ │ ├── .packemon.ts │ │ │ │ └── package.json │ │ └── packemon.config.js │ ├── config-files-polyrepo │ │ ├── package.json │ │ └── packemon.config.ts │ ├── example-solid │ │ ├── package.json │ │ └── solid.tsx │ ├── examples │ │ ├── asset-imports.ts │ │ ├── async-await.ts │ │ ├── cjs-esm-interop.ts │ │ ├── cjs-mjs-wrapper-externals.ts │ │ ├── cjs-mjs-wrapper.ts │ │ ├── data.json │ │ ├── dynamic-imports.ts │ │ ├── externals.ts │ │ ├── generators.ts │ │ ├── helpers.ts │ │ ├── import-attributes.ts │ │ ├── json-imports.ts │ │ ├── logo.svg │ │ ├── namespaces.ts │ │ ├── new-syntax.ts │ │ ├── node-polyfills.ts │ │ ├── package.json │ │ ├── react.tsx │ │ ├── solid.tsx │ │ ├── source-maps.ts │ │ ├── styles.css │ │ └── tsconfig.json │ ├── features │ │ ├── package.json │ │ └── src │ │ │ └── helpers.ts │ ├── project-assets-vanilla │ │ ├── package.json │ │ ├── src │ │ │ ├── index.tsx │ │ │ └── styles.css.ts │ │ └── tsconfig.json │ ├── project-assets │ │ ├── package.json │ │ └── src │ │ │ ├── button │ │ │ ├── index.ts │ │ │ └── styles.css │ │ │ ├── globals.css │ │ │ ├── index.ts │ │ │ └── shared │ │ │ └── fonts.css │ ├── project-bundle │ │ ├── package.json │ │ └── src │ │ │ ├── index.ts │ │ │ ├── not-imported.ts │ │ │ └── other.ts │ ├── project-cjs-compat │ │ ├── package.json │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.cjs.json │ │ └── tsconfig.json │ ├── project-constraint │ │ └── package.json │ ├── project-cts │ │ ├── package.json │ │ ├── src │ │ │ └── index.cts │ │ ├── tsconfig.cjs.json │ │ └── tsconfig.json │ ├── project-mts │ │ ├── package.json │ │ ├── src │ │ │ └── index.mts │ │ ├── tsconfig.json │ │ └── tsconfig.mjs.json │ ├── project-multi-platform │ │ └── package.json │ ├── project-rollup │ │ ├── package.json │ │ ├── src │ │ │ ├── client │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── other │ │ │ │ └── index.ts │ │ │ ├── server │ │ │ │ └── core.ts │ │ │ └── test-utils │ │ │ │ └── base.ts │ │ └── tsconfig.json │ ├── project │ │ ├── package.json │ │ ├── src │ │ │ ├── index.ts │ │ │ └── sub │ │ │ │ └── test.ts │ │ └── tsconfig.json │ ├── validate-entry-bin-object │ │ ├── a.js │ │ ├── b.js │ │ ├── c.js │ │ ├── index.js │ │ └── package.json │ ├── validate-entry-bin │ │ ├── index.js │ │ └── package.json │ ├── validate-entry-browser │ │ ├── index.js │ │ └── package.json │ ├── validate-entry-main │ │ ├── index.js │ │ └── package.json │ ├── validate-entry-man-array │ │ ├── a.js │ │ ├── b.js │ │ ├── c.js │ │ ├── index.js │ │ └── package.json │ ├── validate-entry-man │ │ ├── index.js │ │ └── package.json │ ├── validate-entry-module │ │ ├── index.js │ │ └── package.json │ ├── validate-entry-types │ │ ├── index.d.ts │ │ ├── index.js │ │ └── package.json │ ├── validate-entry-typings │ │ ├── index.d.ts │ │ ├── index.js │ │ └── package.json │ ├── validate-files │ │ ├── cjs │ │ │ └── index.js │ │ ├── esm │ │ │ └── index.js │ │ ├── lib │ │ │ └── index.js │ │ ├── package.json │ │ └── src │ │ │ └── index.ts │ ├── validate-license-file-md │ │ ├── LICENSE.md │ │ └── package.json │ ├── validate-license-file │ │ ├── LICENSE │ │ └── package.json │ ├── validate-readme-file-md │ │ ├── README.md │ │ └── package.json │ ├── validate-readme-file │ │ ├── README │ │ └── package.json │ ├── workspace-not-configured │ │ └── package.json │ ├── workspace-private │ │ └── package.json │ ├── workspaces-configured │ │ ├── package.json │ │ └── packages │ │ │ ├── bar │ │ │ └── package.json │ │ │ ├── baz │ │ │ └── package.json │ │ │ ├── foo │ │ │ └── package.json │ │ │ └── qux │ │ │ └── package.json │ ├── workspaces-feature-flags-root │ │ ├── package.json │ │ ├── packages │ │ │ └── common │ │ │ │ ├── index.ts │ │ │ │ └── package.json │ │ └── tsconfig.json │ ├── workspaces-feature-flags │ │ ├── package.json │ │ └── packages │ │ │ ├── common │ │ │ └── package.json │ │ │ ├── flow │ │ │ └── package.json │ │ │ ├── react-automatic │ │ │ └── package.json │ │ │ ├── react-classic │ │ │ └── package.json │ │ │ ├── react-star │ │ │ └── package.json │ │ │ ├── react │ │ │ └── package.json │ │ │ ├── solid │ │ │ └── package.json │ │ │ ├── ts-config │ │ │ ├── index.ts │ │ │ ├── package.json │ │ │ └── tsconfig.json │ │ │ ├── ts-refs │ │ │ ├── index.ts │ │ │ ├── package.json │ │ │ └── tsconfig.json │ │ │ └── ts │ │ │ └── package.json │ ├── workspaces-no-packages │ │ └── package.json │ ├── workspaces-not-configured │ │ ├── package.json │ │ └── packages │ │ │ ├── bar │ │ │ └── package.json │ │ │ ├── baz │ │ │ └── package.json │ │ │ ├── foo │ │ │ └── package.json │ │ │ └── qux │ │ │ └── package.json │ └── workspaces │ │ ├── lerna.json │ │ ├── package.json │ │ └── packages │ │ ├── invalid-config │ │ └── package.json │ │ ├── no-config │ │ └── package.json │ │ ├── no-package │ │ └── empty │ │ ├── valid-array │ │ └── package.json │ │ ├── valid-object-private │ │ └── package.json │ │ └── valid-object │ │ └── package.json ├── setup.ts ├── testPackemonImports.mjs ├── testPackemonImports.ts └── tsconfig.json ├── tsconfig.eslint.json ├── tsconfig.json ├── tsconfig.options.json ├── vitest.config.ts ├── website ├── .eslintrc.cjs ├── .gitignore ├── CHANGELOG.md ├── babel.config.js ├── docs │ ├── advanced.mdx │ ├── build-workspace.mdx │ ├── build.md │ ├── clean.md │ ├── config.md │ ├── esm.md │ ├── features.md │ ├── files.md │ ├── index.md │ ├── init.md │ ├── install.mdx │ ├── migrate │ │ ├── 2.0.md │ │ ├── 3.0.md │ │ └── 4.0.md │ ├── pack-workspace.mdx │ ├── pack.md │ ├── scaffold.md │ ├── setup.mdx │ ├── swc.md │ ├── validate.md │ └── watch.mdx ├── docusaurus.config.js ├── moon.yml ├── package.json ├── sidebars.js ├── src │ ├── css │ │ └── custom.css │ └── pages │ │ ├── index.tsx │ │ └── styles.module.css ├── static │ ├── .nojekyll │ ├── CNAME │ └── img │ │ ├── cli.png │ │ ├── favicon.svg │ │ ├── logo-bg.svg │ │ └── logo.svg ├── tsconfig.json └── types │ ├── docusaurus.d.ts │ └── global.d.ts └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | cjs/ 4 | coverage/ 5 | dist/ 6 | dts/ 7 | esm/ 8 | lib/ 9 | mjs/ 10 | umd/ 11 | *.d.ts 12 | *.d.cts 13 | *.d.mts 14 | *.min.js 15 | *.map 16 | *.snap 17 | scenarios/ 18 | templates/ 19 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['moon', 'moon/react', 'moon/node'], 4 | parserOptions: { 5 | project: 'tsconfig.eslint.json', 6 | tsconfigRootDir: __dirname, 7 | }, 8 | rules: { 9 | // Doesnt work with `package.json` exports 10 | 'import/no-unresolved': 'off', 11 | // Very buggy? 12 | 'node/no-unpublished-import': 'off', 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: milesjohnson 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | env: 8 | YARN_IGNORE_NODE: 1 9 | jobs: 10 | ci: 11 | name: CI 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | matrix: 15 | os: [ubuntu-latest] # , windows-latest] 16 | node-version: [18, 20] 17 | fail-fast: false # true 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 0 22 | - uses: actions/setup-node@v4 23 | with: 24 | cache: yarn 25 | - uses: moonrepo/tool-version-action@v1 26 | with: 27 | node: ${{ matrix.node-version }} 28 | - run: yarn install --immutable 29 | - run: yarn run setup 30 | - run: yarn run moon --color --log debug ci 31 | integrate: 32 | name: Integrations 33 | runs-on: ubuntu-latest 34 | steps: 35 | - uses: actions/checkout@v4 36 | - uses: actions/setup-node@v4 37 | with: 38 | cache: yarn 39 | - run: yarn install --immutable 40 | - run: yarn run setup 41 | - run: cd scenarios/jest && yarn test 42 | - run: cd scenarios/snowpack && yarn build 43 | - run: cd scenarios/vite && yarn build 44 | - run: cd scenarios/webpack && yarn build 45 | compat: 46 | name: Compatibility 47 | runs-on: ubuntu-latest 48 | steps: 49 | - uses: actions/checkout@v4 50 | with: 51 | fetch-depth: 0 52 | - uses: actions/setup-node@v4 53 | with: 54 | cache: yarn 55 | - run: yarn install --immutable 56 | - run: yarn run setup 57 | - run: yarn run moon run :build 58 | # - run: yarn ts-node --cwd ./tests --transpileOnly ./testPackemonImports.ts 59 | - run: node ./tests/testPackemonImports.mjs 60 | scaffold-mono: 61 | name: Scaffold (monorepo) 62 | runs-on: ubuntu-latest 63 | steps: 64 | - uses: actions/checkout@v4 65 | with: 66 | fetch-depth: 0 67 | - uses: actions/setup-node@v4 68 | with: 69 | cache: yarn 70 | - run: yarn install --immutable 71 | - run: yarn run setup 72 | - run: mkdir ../work 73 | - run: yarn packemon scaffold --packageManager npm --template monorepo ../work 74 | - run: yarn packemon scaffold --packageManager npm --template monorepo-package ../work 75 | # Test all the things 76 | - run: cd ../work 77 | - run: ls -R . 78 | - run: yarn run moon check --all 79 | scaffold-poly: 80 | name: Scaffold (polyrepo) 81 | runs-on: ubuntu-latest 82 | steps: 83 | - uses: actions/checkout@v4 84 | with: 85 | fetch-depth: 0 86 | - uses: actions/setup-node@v4 87 | with: 88 | cache: yarn 89 | - run: yarn install --immutable 90 | - run: yarn run setup 91 | - run: mkdir ../work 92 | - run: yarn packemon scaffold --packageManager npm --template polyrepo ../work 93 | - run: yarn packemon scaffold --packageManager npm --template polyrepo-package ../work 94 | # Test all the things 95 | - run: cd ../work 96 | - run: ls -R . 97 | - run: yarn run moon check --all 98 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | docs: 8 | name: Docs 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: actions/setup-node@v4 13 | with: 14 | cache: yarn 15 | - name: Add key to allow access to repository 16 | env: 17 | SSH_AUTH_SOCK: /tmp/ssh_agent.sock 18 | run: | 19 | mkdir -p ~/.ssh 20 | ssh-keyscan github.com >> ~/.ssh/known_hosts 21 | echo "${{ secrets.GH_PAGES_DEPLOY }}" > ~/.ssh/id_rsa 22 | chmod 600 ~/.ssh/id_rsa 23 | cat <> ~/.ssh/config 24 | Host github.com 25 | HostName github.com 26 | IdentityFile ~/.ssh/id_rsa 27 | EOT 28 | - name: Deploy to GitHub Pages 29 | env: 30 | GIT_USER: milesj 31 | USE_SSH: true 32 | run: | 33 | git config --global user.email "actions@github.com" 34 | git config --global user.name "gh-actions" 35 | yarn install --immutable 36 | yarn setup 37 | yarn run moon run website:deploy 38 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: PR 2 | on: pull_request 3 | jobs: 4 | conventional: 5 | name: Conventional Title 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | - uses: actions/setup-node@v4 10 | with: 11 | cache: yarn 12 | - uses: beemojs/conventional-pr-action@v2 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs/ 3 | *.log 4 | 5 | # Cache 6 | .docusaurus 7 | .eslintcache 8 | .idea 9 | .npm 10 | .vscode 11 | .yarnclean 12 | .parcel-cache 13 | 14 | # Directories 15 | assets/ 16 | build/ 17 | coverage/ 18 | cjs/ 19 | dist/ 20 | dts/ 21 | esm/ 22 | lib/ 23 | mjs/ 24 | tmp/ 25 | umd/ 26 | node_modules/ 27 | test/ 28 | scenarios/*/build.js 29 | 30 | # Required for testing 31 | !tests/__fixtures__/project/dts 32 | !tests/__fixtures__/validate-files/cjs 33 | !tests/__fixtures__/validate-files/esm 34 | !tests/__fixtures__/validate-files/lib 35 | scripts/swc 36 | 37 | # Custom 38 | *.min.js 39 | *.map 40 | 41 | # Configs 42 | .flowconfig 43 | /webpack.config.js 44 | *.tsbuildinfo 45 | 46 | # Yarn 47 | .yarn/* 48 | !.yarn/patches 49 | !.yarn/releases 50 | !.yarn/plugins 51 | !.yarn/sdks 52 | !.yarn/versions 53 | .pnp.* 54 | 55 | # moon 56 | .moon/cache 57 | .moon/docker 58 | -------------------------------------------------------------------------------- /.moon/tasks.yml: -------------------------------------------------------------------------------- 1 | $schema: 'https://moonrepo.dev/schemas/tasks.json' 2 | 3 | extends: 'https://raw.githubusercontent.com/moonrepo/dev/master/.moon/tasks/node.yml' 4 | 5 | tasks: 6 | build: 7 | command: 'packemon' 8 | args: 9 | - 'build' 10 | - '--addEngines' 11 | - '--addExports' 12 | - '--declaration' 13 | inputs: 14 | - '@globs(sources)' 15 | - '@globs(typescript)' 16 | - 'package.json' 17 | outputs: 18 | - 'lib' 19 | deps: 20 | - '^:build' 21 | env: 22 | NODE_ENV: 'production' 23 | 24 | test: 25 | command: 'vitest run --config ../../vitest.config.ts' 26 | deps: 27 | - '^:build' 28 | -------------------------------------------------------------------------------- /.moon/toolchain.yml: -------------------------------------------------------------------------------- 1 | $schema: 'https://moonrepo.dev/schemas/toolchain.json' 2 | 3 | extends: 'https://raw.githubusercontent.com/moonrepo/dev/master/.moon/toolchain.yml' 4 | 5 | node: 6 | yarn: 7 | version: '4.1.0' 8 | -------------------------------------------------------------------------------- /.moon/workspace.yml: -------------------------------------------------------------------------------- 1 | $schema: 'https://moonrepo.dev/schemas/workspace.json' 2 | 3 | extends: 'https://raw.githubusercontent.com/moonrepo/dev/master/.moon/workspace.yml' 4 | 5 | projects: 6 | - 'packages/*' 7 | - 'website' 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | website/ 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18.14.0 -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .docusaurus 2 | .github/ 3 | .yarn/ 4 | node_modules/ 5 | build/ 6 | cjs/ 7 | coverage/ 8 | dist/ 9 | dts/ 10 | esm/ 11 | lib/ 12 | mjs/ 13 | umd/ 14 | templates/ 15 | CHANGELOG.md 16 | *.d.ts 17 | *.d.cts 18 | *.d.mts 19 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = 'prettier-config-moon'; 2 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | compressionLevel: mixed 2 | 3 | enableGlobalCache: true 4 | 5 | enableTelemetry: false 6 | 7 | nodeLinker: node-modules 8 | 9 | yarnPath: .yarn/releases/yarn-4.1.0.cjs 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Miles Johnson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Packemon 2 | 3 | [![Build Status](https://github.com/milesj/packemon/workflows/Build/badge.svg)](https://github.com/milesj/packemon/actions?query=branch%3Amaster) 4 | [![npm version](https://badge.fury.io/js/packemon.svg)](https://www.npmjs.com/package/packemon) 5 | 6 | > Gotta pack 'em all! 7 | 8 | Are you a library maintainer? Confused on how to build packages for consumers? Unsure of what 9 | tooling and plugins to use? What about CommonJS vs ECMAScript? TypeScript, JavaScript, or FlowType? 10 | Forget that headache and let Packemon do the heavy lifting for you. No need to fiddle with Babel or 11 | Rollup configurations! 12 | 13 | Packemon is a "batteries included" CLI that will prepare each package for distribution by building 14 | with the proper tooling and plugins, provide sane defaults and configurations, verify package 15 | requirements, and much more! By default Packemon will generate ECMAScript modules, but can be 16 | configured to support all formats. 17 | 18 | ## Features 19 | 20 | - Scaffold TypeScript packages, in either a monorepo or polyrepo project setup. 21 | - Configure packages for Node.js, Web browsers, or React Native, with multiple output formats like 22 | CommonJS and ECMAScript (default). 23 | - Build packages with Rollup to create self-contained and tree-shaken bundles. Provide the smallest 24 | file sizes possible! 25 | - Support a single index import, multiple imports, deep imports, or any kind of entry point. 26 | - Transform packages with Babel's `preset-env` and the configured platform targets. Only ship and 27 | polyfill what's truly necessary! 28 | - Generate and combine TypeScript declarations into a single public-only API representation. 29 | - Generate compact source maps for platform + format based builds. 30 | 31 | ## Requirements 32 | 33 | - Linux, OSX, Windows 34 | - Node 18.12+ 35 | 36 | ## Documentation 37 | 38 | [https://packemon.dev](https://packemon.dev) 39 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "independent", 3 | "npmClient": "yarn", 4 | "packages": [ 5 | "packages/*" 6 | ], 7 | "command": { 8 | "publish": { 9 | "ignoreChanges": [ 10 | "*.md" 11 | ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "packemon-root", 4 | "scripts": { 5 | "docs": "yarn run moon run website:start", 6 | "moon": "$(yarn bin moon)", 7 | "clean": "rm -rf packages/*/{cjs,lib,mjs}", 8 | "packemon": "node ./packages/packemon/mjs/bin.mjs", 9 | "prerelease": "yarn run setup && yarn run moon run :build -u", 10 | "release": "yarn prerelease && lerna-release", 11 | "setup": "node ./scripts/buildPackages.mjs", 12 | "version": "yarn install && git add yarn.lock", 13 | "postinstall": "node ./scripts/deleteSwcTypes.mjs" 14 | }, 15 | "workspaces": [ 16 | "packages/*", 17 | "scenarios/*", 18 | "website" 19 | ], 20 | "engines": { 21 | "node": ">=16.12.0" 22 | }, 23 | "devDependencies": { 24 | "@babel/cli": "^7.24.6", 25 | "@babel/core": "^7.24.6", 26 | "@moonrepo/cli": "^1.25.0", 27 | "@moonrepo/dev": "^3.0.1", 28 | "@swc/cli": "^0.3.12", 29 | "@types/micromatch": "^4.0.7", 30 | "@types/node": "^20.12.12", 31 | "@types/react": "^18.3.2", 32 | "@types/semver": "^7.5.8", 33 | "@vanilla-extract/rollup-plugin": "^1.3.5", 34 | "babel-preset-moon": "^3.0.2", 35 | "bootstrap": "^5.3.3", 36 | "chokidar": "^3.6.0", 37 | "electron-to-chromium": "^1.4.783", 38 | "eslint": "^8.57.0", 39 | "eslint-config-moon": "^3.1.1", 40 | "lerna": "^8.1.3", 41 | "prettier": "^3.2.5", 42 | "prettier-config-moon": "^1.1.2", 43 | "ts-node": "^10.9.2", 44 | "tsconfig-moon": "^1.3.0", 45 | "typescript": "^5.4.5", 46 | "vitest": "^1.6.0" 47 | }, 48 | "funding": { 49 | "type": "ko-fi", 50 | "url": "https://ko-fi.com/milesjohnson" 51 | }, 52 | "packageManager": "yarn@4.1.0" 53 | } 54 | -------------------------------------------------------------------------------- /packages/babel-plugin-cjs-esm-interop/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Miles Johnson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/babel-plugin-cjs-esm-interop/README.md: -------------------------------------------------------------------------------- 1 | # babel-plugin-cjs-esm-interop 2 | 3 | [![Build Status](https://github.com/milesj/packemon/workflows/Build/badge.svg)](https://github.com/milesj/packemon/actions?query=branch%3Amaster) 4 | [![npm version](https://badge.fury.io/js/babel-plugin-cjs-esm-interop.svg)](https://www.npmjs.com/package/babel-plugin-cjs-esm-interop) 5 | 6 | Transform the differences between CommonJS code (`.js`, `.cjs`) and ECMAScript module code (`.mjs`), 7 | based on the 8 | [official Node.js documentation](https://nodejs.org/api/esm.html#esm_differences_between_es_modules_and_commonjs). 9 | 10 | > We suggest writing your code as ESM (`.mjs`, `.ts`, `.tsx`, `.js` with module type) and compiling 11 | > down to CJS instead of the reverse. This means using new syntax like `import.meta`, `import()`, 12 | > etc! 13 | 14 | ```ts 15 | // Input: mjs 16 | const self = import.meta.url; 17 | ``` 18 | 19 | ```ts 20 | // Output: cjs 21 | const self = __filename; 22 | ``` 23 | 24 | ## Installation 25 | 26 | ``` 27 | yarn add --dev babel-plugin-cjs-esm-interop 28 | ``` 29 | 30 | Add the plugin to your root `babel.config.*` file and configure the output `format` option with 31 | either `mjs` (default) or `cjs`. 32 | 33 | ```js 34 | module.exports = { 35 | plugins: [['babel-plugin-cjs-esm-interop', { format: 'mjs' }]], 36 | }; 37 | ``` 38 | 39 | ## Requirements 40 | 41 | - Linux, OSX, Windows 42 | - Node 18.12+ 43 | 44 | ## Transforms 45 | 46 | The following transforms and error handling are applied. 47 | 48 | ### CJS output 49 | 50 | - `export`, `export default` 51 | - Will throw an error if used. 52 | - `import.meta.url` -> `__filename` (with `file://` contextually) 53 | - `path.dirname(import.meta.url)` -> `__dirname` (with `file://` contextually) 54 | 55 | ### MJS output 56 | 57 | - `require()`, `require.resolve()`, `require.extensions`, `require.cache`, `exports.`, 58 | `module.exports`, `process.env.NODE_PATH` 59 | - Will throw an error if used. 60 | - `__filename` -> `import.meta.url` 61 | - `__dirname` -> `path.dirname(import.meta.url)` 62 | 63 | ## How to's 64 | 65 | If you want to support ESM code, you'll need to move away from certain features, in which you have a 66 | few options. 67 | 68 | ### What to replace `require()` with? 69 | 70 | - Use `import()` for JS files. Be aware that this is _async_ and cannot be used in the module scope 71 | unless top-level await is supported. 72 | - Use the `fs` module for JSON files: `JSON.parse(fs.readFileSync(path))` 73 | - Use `module.createRequire()`, which returns a require-like function you may use. 74 | [More info](https://nodejs.org/api/module.html#module_module_createrequire_filename). 75 | 76 | ### What to replace `require.resolve()` with? 77 | 78 | - Use the [`resolve`](https://www.npmjs.com/package/resolve) npm package. 79 | - Use `module.createRequire()`, which returns a require-like function with a resolver you may use. 80 | [More info](https://nodejs.org/api/module.html#module_module_createrequire_filename). 81 | -------------------------------------------------------------------------------- /packages/babel-plugin-cjs-esm-interop/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-cjs-esm-interop", 3 | "version": "4.0.2", 4 | "description": "Transform CJS to ESM and vice versa.", 5 | "keywords": [ 6 | "babel", 7 | "plugin", 8 | "cjs", 9 | "mjs", 10 | "interop", 11 | "convert", 12 | "transform" 13 | ], 14 | "repository": { 15 | "type": "git", 16 | "url": "git@github.com:milesj/packemon.git", 17 | "directory": "packages/babel-plugin-cjs-esm-interop" 18 | }, 19 | "author": "Miles Johnson", 20 | "license": "MIT", 21 | "main": "./lib/index.js", 22 | "types": "./lib/index.d.ts", 23 | "files": [ 24 | "lib/**/*", 25 | "src/**/*" 26 | ], 27 | "engines": { 28 | "node": ">=18.12.0" 29 | }, 30 | "packemon": { 31 | "format": "lib", 32 | "platform": "node" 33 | }, 34 | "peerDependencies": { 35 | "@babel/core": "^7.0.0" 36 | }, 37 | "dependencies": { 38 | "@babel/helper-module-imports": "^7.24.6" 39 | }, 40 | "funding": { 41 | "type": "ko-fi", 42 | "url": "https://ko-fi.com/milesjohnson" 43 | }, 44 | "exports": { 45 | "./package.json": "./package.json", 46 | "./*": { 47 | "types": "./lib/*.d.ts", 48 | "default": "./lib/*.js" 49 | }, 50 | ".": { 51 | "types": "./lib/index.d.ts", 52 | "default": "./lib/index.js" 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/babel-plugin-cjs-esm-interop/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.options.json", 3 | "compilerOptions": { 4 | "outDir": "../../.moon/cache/types/packages/babel-plugin-cjs-esm-interop" 5 | }, 6 | "include": [ 7 | "src/**/*", 8 | "tests/**/*", 9 | "types/**/*", 10 | "../../types/**/*" 11 | ], 12 | "references": [ 13 | { 14 | "path": "./tsconfig.lib.json" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /packages/babel-plugin-cjs-esm-interop/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.options.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "include": [ 8 | "src/**/*" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/babel-plugin-conditional-invariant/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Miles Johnson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/babel-plugin-conditional-invariant/README.md: -------------------------------------------------------------------------------- 1 | # babel-plugin-conditional-invariant 2 | 3 | [![Build Status](https://github.com/milesj/packemon/workflows/Build/badge.svg)](https://github.com/milesj/packemon/actions?query=branch%3Amaster) 4 | [![npm version](https://badge.fury.io/js/babel-plugin-conditional-invariant.svg)](https://www.npmjs.com/package/babel-plugin-conditional-invariant) 5 | 6 | Wrap invariant function checks in `process.env.NODE_ENV` conditionals that only run in development. 7 | 8 | ```ts 9 | // Input 10 | invariant(value === false, 'Value must be falsy!'); 11 | ``` 12 | 13 | ```ts 14 | // Output 15 | if (process.env.NODE_ENV !== 'production') { 16 | invariant(value === false, 'Value must be falsy!'); 17 | } 18 | ``` 19 | 20 | ## Installation 21 | 22 | ``` 23 | yarn add --dev babel-plugin-conditional-invariant 24 | ``` 25 | 26 | Add the plugin to your root `babel.config.*` file. 27 | 28 | ```js 29 | module.exports = { 30 | plugins: ['babel-plugin-conditional-invariant'], 31 | }; 32 | ``` 33 | 34 | ## Requirements 35 | 36 | - Linux, OSX, Windows 37 | - Node 18.12+ 38 | -------------------------------------------------------------------------------- /packages/babel-plugin-conditional-invariant/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-conditional-invariant", 3 | "release": "1626547876981", 4 | "version": "4.0.1", 5 | "description": "Wrap invariant checks in NODE_ENV conditionals.", 6 | "keywords": [ 7 | "babel", 8 | "plugin", 9 | "env", 10 | "invariant", 11 | "conditionals", 12 | "expressions" 13 | ], 14 | "repository": { 15 | "type": "git", 16 | "url": "git@github.com:milesj/packemon.git", 17 | "directory": "packages/babel-plugin-conditional-invariant" 18 | }, 19 | "author": "Miles Johnson", 20 | "license": "MIT", 21 | "main": "./lib/index.js", 22 | "types": "./lib/index.d.ts", 23 | "files": [ 24 | "lib/**/*", 25 | "src/**/*" 26 | ], 27 | "engines": { 28 | "node": ">=18.12.0" 29 | }, 30 | "packemon": { 31 | "format": "lib", 32 | "platform": "node" 33 | }, 34 | "peerDependencies": { 35 | "@babel/core": "^7.0.0" 36 | }, 37 | "funding": { 38 | "type": "ko-fi", 39 | "url": "https://ko-fi.com/milesjohnson" 40 | }, 41 | "exports": { 42 | "./package.json": "./package.json", 43 | "./*": { 44 | "types": "./lib/*.d.ts", 45 | "default": "./lib/*.js" 46 | }, 47 | ".": { 48 | "types": "./lib/index.d.ts", 49 | "default": "./lib/index.js" 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/babel-plugin-conditional-invariant/src/index.ts: -------------------------------------------------------------------------------- 1 | import { NodePath, type PluginObj, types as t } from '@babel/core'; 2 | 3 | function isWrappedWithConditional(path: NodePath): boolean { 4 | let currentPath: NodePath | null = path; 5 | let wrapped = false; 6 | 7 | while (currentPath) { 8 | // Is there a cleaner way of doing this??? 9 | if ( 10 | currentPath.isIfStatement() && 11 | currentPath.get('test').isBinaryExpression({ operator: '!==' }) && 12 | (currentPath.get('test.left') as NodePath).isMemberExpression() && 13 | (currentPath.get('test.left.object') as NodePath).isMemberExpression() && 14 | (currentPath.get('test.left.object.object') as NodePath).isIdentifier({ 15 | name: 'process', 16 | }) && 17 | (currentPath.get('test.left.object.property') as NodePath).isIdentifier({ 18 | name: 'env', 19 | }) && 20 | (currentPath.get('test.left.property') as NodePath).isIdentifier({ 21 | name: 'NODE_ENV', 22 | }) && 23 | (currentPath.get('test.right') as NodePath).isStringLiteral({ 24 | value: 'production', 25 | }) 26 | ) { 27 | wrapped = true; 28 | break; 29 | } 30 | 31 | currentPath = currentPath.parentPath; 32 | } 33 | 34 | return wrapped; 35 | } 36 | 37 | export default function conditionalInvariantPlugin(): PluginObj { 38 | return { 39 | visitor: { 40 | ExpressionStatement: { 41 | enter(path: NodePath) { 42 | if ( 43 | !path.get('expression').isCallExpression() || 44 | !(path.get('expression.callee') as NodePath).isIdentifier({ 45 | name: 'invariant', 46 | }) 47 | ) { 48 | return; 49 | } 50 | 51 | // Only transform invariants within the following scenarios: 52 | // - Block statements (if, while, functions, etc) 53 | // - Switch case statements 54 | // - Module scope 55 | if ( 56 | !path.parentPath.isBlockStatement() && 57 | !path.parentPath.isSwitchCase() && 58 | !path.parentPath.isProgram() 59 | ) { 60 | return; 61 | } 62 | 63 | // Dont replace with new syntax if invariant is already wrapped 64 | // in a NODE_ENV conditional! 65 | if (isWrappedWithConditional(path)) { 66 | return; 67 | } 68 | 69 | path.replaceWith( 70 | t.ifStatement( 71 | t.binaryExpression( 72 | '!==', 73 | t.memberExpression( 74 | t.memberExpression(t.identifier('process'), t.identifier('env'), false), 75 | t.identifier('NODE_ENV'), 76 | false, 77 | ), 78 | t.stringLiteral('production'), 79 | ), 80 | t.blockStatement([path.node]), 81 | ), 82 | ); 83 | }, 84 | }, 85 | }, 86 | }; 87 | } 88 | -------------------------------------------------------------------------------- /packages/babel-plugin-conditional-invariant/tests/__snapshots__/plugin.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`conditionalInvariantPlugin() > will not transform if wrapped in a matching conditional 1`] = ` 4 | "if (process.env.NODE_ENV !== 'production') { 5 | invariant(); 6 | }" 7 | `; 8 | 9 | exports[`conditionalInvariantPlugin() > will not transform if wrapped in a matching conditional that is layers deep 1`] = ` 10 | "if (process.env.NODE_ENV !== 'production') { 11 | if (true) { 12 | if (false) { 13 | invariant(); 14 | } 15 | } 16 | }" 17 | `; 18 | 19 | exports[`conditionalInvariantPlugin() > will transform if wrapped in a non-matching conditional 1`] = ` 20 | "if (process.env.NODE_ENV === 'development') { 21 | if (process.env.NODE_ENV !== 'production') { 22 | invariant(); 23 | } 24 | }" 25 | `; 26 | 27 | exports[`conditionalInvariantPlugin() > will transform if wrapped in a non-matching conditional that is layers deep 1`] = ` 28 | "if (process.env.NODE_ENV === 'development') { 29 | if (true) { 30 | if (false) { 31 | if (process.env.NODE_ENV !== 'production') { 32 | invariant(); 33 | } 34 | } 35 | } 36 | }" 37 | `; 38 | 39 | exports[`conditionalInvariantPlugin() > will transform valid expressions 1`] = ` 40 | "if (process.env.NODE_ENV !== 'production') { 41 | invariant(); 42 | } 43 | if (process.env.NODE_ENV !== 'production') { 44 | invariant(false); 45 | } 46 | while (true) { 47 | if (process.env.NODE_ENV !== 'production') { 48 | invariant(true); 49 | } 50 | } 51 | do { 52 | if (process.env.NODE_ENV !== 'production') { 53 | invariant(value === false, 'Some message'); 54 | } 55 | } while (true); 56 | switch (value) { 57 | case 1: 58 | if (process.env.NODE_ENV !== 'production') { 59 | invariant(value === true, 'Some message'); 60 | } 61 | break; 62 | case 2: 63 | { 64 | if (process.env.NODE_ENV !== 'production') { 65 | invariant(value === true, 'Some message'); 66 | } 67 | break; 68 | } 69 | } 70 | if (1) { 71 | if (process.env.NODE_ENV !== 'production') { 72 | invariant(!value, 'Some message', 'another arg'); 73 | } 74 | } else if (2) { 75 | if (process.env.NODE_ENV !== 'production') { 76 | invariant(!value, 'Some message', 123); 77 | } 78 | } else { 79 | if (process.env.NODE_ENV !== 'production') { 80 | invariant(!!value, 'Some message', true, 456); 81 | } 82 | }" 83 | `; 84 | -------------------------------------------------------------------------------- /packages/babel-plugin-conditional-invariant/tests/plugin.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { transformAsync, type TransformOptions } from '@babel/core'; 3 | import conditionalInvariantPlugin from '../src'; 4 | 5 | async function transform(code: string, options?: TransformOptions): Promise { 6 | const result = await transformAsync(code, { 7 | babelrc: false, 8 | comments: false, 9 | configFile: false, 10 | filename: 'file.js', 11 | plugins: [conditionalInvariantPlugin()], 12 | presets: ['@babel/preset-react'], 13 | generatorOpts: { 14 | jsescOption: { quotes: 'single' }, 15 | }, 16 | ...options, 17 | }); 18 | 19 | return result?.code ?? ''; 20 | } 21 | 22 | describe('conditionalInvariantPlugin()', () => { 23 | it(`will transform valid expressions`, async () => { 24 | await expect( 25 | transform(` 26 | invariant(); 27 | 28 | invariant(false); 29 | 30 | while (true) { 31 | invariant(true); 32 | } 33 | 34 | do { 35 | invariant(value === false, 'Some message'); 36 | } while (true); 37 | 38 | switch (value) { 39 | case 1: 40 | invariant(value === true, 'Some message'); 41 | break; 42 | 43 | case 2: { 44 | invariant(value === true, 'Some message'); 45 | break; 46 | } 47 | } 48 | 49 | if (1) { 50 | invariant(!value, 'Some message', 'another arg'); 51 | } else if (2) { 52 | invariant(!value, 'Some message', 123); 53 | } else { 54 | invariant(!!value, 'Some message', true, 456); 55 | } 56 | `), 57 | ).resolves.toMatchSnapshot(); 58 | }); 59 | 60 | it(`will transform if wrapped in a non-matching conditional`, async () => { 61 | await expect( 62 | transform(` 63 | if (process.env.NODE_ENV === 'development') { 64 | invariant(); 65 | } 66 | `), 67 | ).resolves.toMatchSnapshot(); 68 | }); 69 | 70 | it(`will transform if wrapped in a non-matching conditional that is layers deep`, async () => { 71 | await expect( 72 | transform(` 73 | if (process.env.NODE_ENV === 'development') { 74 | if (true) { 75 | if (false) { 76 | invariant(); 77 | } 78 | } 79 | } 80 | `), 81 | ).resolves.toMatchSnapshot(); 82 | }); 83 | 84 | it(`will not transform if wrapped in a matching conditional`, async () => { 85 | await expect( 86 | transform(` 87 | if (process.env.NODE_ENV !== 'production') { 88 | invariant(); 89 | } 90 | `), 91 | ).resolves.toMatchSnapshot(); 92 | }); 93 | 94 | it(`will not transform if wrapped in a matching conditional that is layers deep`, async () => { 95 | await expect( 96 | transform(` 97 | if (process.env.NODE_ENV !== 'production') { 98 | if (true) { 99 | if (false) { 100 | invariant(); 101 | } 102 | } 103 | } 104 | `), 105 | ).resolves.toMatchSnapshot(); 106 | }); 107 | }); 108 | -------------------------------------------------------------------------------- /packages/babel-plugin-conditional-invariant/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.options.json", 3 | "compilerOptions": { 4 | "outDir": "../../.moon/cache/types/packages/babel-plugin-conditional-invariant" 5 | }, 6 | "include": [ 7 | "src/**/*", 8 | "tests/**/*", 9 | "types/**/*", 10 | "../../types/**/*" 11 | ], 12 | "references": [ 13 | { 14 | "path": "./tsconfig.lib.json" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /packages/babel-plugin-conditional-invariant/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.options.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "include": [ 8 | "src/**/*" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/babel-plugin-env-constants/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Miles Johnson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/babel-plugin-env-constants/README.md: -------------------------------------------------------------------------------- 1 | # babel-plugin-env-constants 2 | 3 | [![Build Status](https://github.com/milesj/packemon/workflows/Build/badge.svg)](https://github.com/milesj/packemon/actions?query=branch%3Amaster) 4 | [![npm version](https://badge.fury.io/js/babel-plugin-env-constants.svg)](https://www.npmjs.com/package/babel-plugin-env-constants) 5 | 6 | Transform `__DEV__`, `__PROD__`, and `__TEST__` constants to `process.env.NODE_ENV` conditionals. 7 | 8 | ```ts 9 | // Input 10 | if (__DEV__) { 11 | console.log('Some message in development!'); 12 | } 13 | 14 | const value = __TEST__ ? 0 : 100; 15 | ``` 16 | 17 | ```ts 18 | // Output 19 | if (process.env.NODE_ENV !== 'production') { 20 | console.log('Some message in development!'); 21 | } 22 | 23 | const value = process.env.NODE_ENV === 'test' ? 0 : 100; 24 | ``` 25 | 26 | ## Installation 27 | 28 | ``` 29 | yarn add --dev babel-plugin-env-constants 30 | ``` 31 | 32 | Add the plugin to your root `babel.config.*` file. 33 | 34 | ```js 35 | module.exports = { 36 | plugins: ['babel-plugin-env-constants'], 37 | }; 38 | ``` 39 | 40 | And if you are using TypeScript, you'll most likely need to declare the globals yourself. 41 | 42 | ```ts 43 | declare global { 44 | const __DEV__: boolean; 45 | const __PROD__: boolean; 46 | const __TEST__: boolean; 47 | } 48 | ``` 49 | 50 | ## Requirements 51 | 52 | - Linux, OSX, Windows 53 | - Node 18.12+ 54 | -------------------------------------------------------------------------------- /packages/babel-plugin-env-constants/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-env-constants", 3 | "release": "1626547876981", 4 | "version": "4.0.1", 5 | "description": "Transform __DEV__, __PROD__, and __TEST__ constants to NODE_ENV conditionals.", 6 | "keywords": [ 7 | "babel", 8 | "plugin", 9 | "env", 10 | "constants", 11 | "conditionals", 12 | "expressions" 13 | ], 14 | "repository": { 15 | "type": "git", 16 | "url": "git@github.com:milesj/packemon.git", 17 | "directory": "packages/babel-plugin-env-constants" 18 | }, 19 | "author": "Miles Johnson", 20 | "license": "MIT", 21 | "main": "./lib/index.js", 22 | "types": "./lib/index.d.ts", 23 | "files": [ 24 | "lib/**/*", 25 | "src/**/*" 26 | ], 27 | "engines": { 28 | "node": ">=18.12.0" 29 | }, 30 | "packemon": { 31 | "format": "lib", 32 | "platform": "node" 33 | }, 34 | "peerDependencies": { 35 | "@babel/core": "^7.0.0" 36 | }, 37 | "funding": { 38 | "type": "ko-fi", 39 | "url": "https://ko-fi.com/milesjohnson" 40 | }, 41 | "exports": { 42 | "./package.json": "./package.json", 43 | "./*": { 44 | "types": "./lib/*.d.ts", 45 | "default": "./lib/*.js" 46 | }, 47 | ".": { 48 | "types": "./lib/index.d.ts", 49 | "default": "./lib/index.js" 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/babel-plugin-env-constants/src/index.ts: -------------------------------------------------------------------------------- 1 | import './types'; 2 | import { NodePath, type PluginObj, types as t } from '@babel/core'; 3 | 4 | const exprs = { 5 | DEV: ['!==', 'production'], 6 | PROD: ['===', 'production'], 7 | TEST: ['===', 'test'], 8 | }; 9 | 10 | export default function envConstantsPlugin(): PluginObj { 11 | return { 12 | visitor: { 13 | Identifier: { 14 | enter(path: NodePath) { 15 | Object.entries(exprs).forEach(([expr, [op, env]]) => { 16 | if (!path.isIdentifier({ name: `__${expr}__` })) { 17 | return; 18 | } 19 | 20 | // const __DEV__ = var; 21 | if (path.parentPath.isVariableDeclarator()) { 22 | return; 23 | } 24 | 25 | // { __DEV__: var } 26 | // { [__DEV__]: var } 27 | if (path.parentPath.isObjectProperty() && path.parentPath.node.value !== path.node) { 28 | return; 29 | } 30 | 31 | // obj.__DEV__ = var; 32 | if (path.parentPath.isMemberExpression()) { 33 | return; 34 | } 35 | 36 | path.replaceWith( 37 | t.binaryExpression( 38 | op as '===', 39 | t.memberExpression( 40 | t.memberExpression(t.identifier('process'), t.identifier('env'), false), 41 | t.identifier('NODE_ENV'), 42 | false, 43 | ), 44 | t.stringLiteral(env), 45 | ), 46 | ); 47 | }); 48 | }, 49 | }, 50 | }, 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /packages/babel-plugin-env-constants/src/types.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-var */ 2 | /* eslint-disable vars-on-top */ 3 | /* eslint-disable no-underscore-dangle */ 4 | 5 | declare global { 6 | var __DEV__: boolean; 7 | var __PROD__: boolean; 8 | var __TEST__: boolean; 9 | } 10 | 11 | export type {}; 12 | -------------------------------------------------------------------------------- /packages/babel-plugin-env-constants/tests/plugin.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { transformAsync, type TransformOptions } from '@babel/core'; 3 | import envConstantsPlugin from '../src'; 4 | 5 | async function transform(code: string, options?: TransformOptions): Promise { 6 | const result = await transformAsync(code, { 7 | babelrc: false, 8 | comments: false, 9 | configFile: false, 10 | filename: 'file.js', 11 | plugins: [envConstantsPlugin()], 12 | presets: ['@babel/preset-react'], 13 | generatorOpts: { 14 | jsescOption: { quotes: 'single' }, 15 | }, 16 | ...options, 17 | }); 18 | 19 | return result?.code ?? ''; 20 | } 21 | 22 | describe('envConstantsPlugin()', () => { 23 | ['DEV', 'PROD', 'TEST'].forEach((name) => { 24 | const expr = `__${name}__`; 25 | 26 | it(`transforms ${expr} expressions`, async () => { 27 | await expect( 28 | transform(` 29 | if (${expr}) { 30 | } else if (${expr} && 123) { 31 | } else if (true || ${expr}) { 32 | } else if (!${expr}) { 33 | } else {} 34 | 35 | switch (${expr}) {} 36 | 37 | while (${expr}) {} 38 | 39 | do {} while (${expr}); 40 | 41 | const ternary = ${expr} ? true : false; 42 | 43 | const objectValue = { 44 | foo: ${expr}, 45 | }; 46 | 47 | const arrayValue = [${expr}]; 48 | 49 | {${expr} ? 'Child' : null};`), 50 | ).resolves.toMatchSnapshot(); 51 | }); 52 | 53 | it(`will not transform invalid ${expr} expressions`, async () => { 54 | await expect( 55 | transform(` 56 | const ${expr} = 123; 57 | 58 | const objectProperty = { ${expr}: true }; 59 | 60 | const objectComputed = { [${expr}]: false }; 61 | 62 | objectProperty.${expr} = false; 63 | objectComputed[${expr}] = false; 64 | 65 | const arrayIndex = []; 66 | arrayIndex[${expr}] = 1;`), 67 | ).resolves.toMatchSnapshot(); 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /packages/babel-plugin-env-constants/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.options.json", 3 | "compilerOptions": { 4 | "outDir": "../../.moon/cache/types/packages/babel-plugin-env-constants" 5 | }, 6 | "include": [ 7 | "src/**/*", 8 | "tests/**/*", 9 | "types/**/*", 10 | "../../types/**/*" 11 | ], 12 | "references": [ 13 | { 14 | "path": "./tsconfig.lib.json" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /packages/babel-plugin-env-constants/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.options.json", 3 | "compilerOptions": { 4 | "outDir": "lib", 5 | "rootDir": "src" 6 | }, 7 | "include": [ 8 | "src/**/*" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/packemon/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | 'no-console': 'off', 4 | 'no-magic-numbers': 'off', 5 | 'no-use-before-define': 'off', 6 | 'sort-keys': 'off', 7 | 'promise/always-return': 'off', 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/packemon/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Miles Johnson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/packemon/README.md: -------------------------------------------------------------------------------- 1 | # Packemon 2 | 3 | [![Build Status](https://github.com/milesj/packemon/workflows/Build/badge.svg)](https://github.com/milesj/packemon/actions?query=branch%3Amaster) 4 | [![npm version](https://badge.fury.io/js/packemon.svg)](https://www.npmjs.com/package/packemon) 5 | 6 | > Gotta pack 'em all! 7 | 8 | Are you a library maintainer? Confused on how to build packages for consumers? Unsure of what 9 | tooling and plugins to use? What about CommonJS vs ECMAScript? TypeScript, JavaScript, or FlowType? 10 | Forget that headache and let Packemon do the heavy lifting for you. No need to fiddle with Babel or 11 | Rollup configurations! 12 | 13 | Packemon is a "batteries included" CLI that will prepare each package for distribution by building 14 | with the proper tooling and plugins, provide sane defaults and configurations, verify package 15 | requirements, and much more! By default Packemon will generate ECMAScript modules, but can be 16 | configured to support all formats. 17 | 18 | ## Features 19 | 20 | - Scaffold TypeScript packages, in either a monorepo or polyrepo project setup. 21 | - Configure packages for Node.js, Web browsers, or React Native, with multiple output formats like 22 | CommonJS and ECMAScript (default). 23 | - Build packages with Rollup to create self-contained and tree-shaken bundles. Provide the smallest 24 | file sizes possible! 25 | - Support a single index import, multiple imports, deep imports, or any kind of entry point. 26 | - Transform packages with Babel's `preset-env` and the configured platform targets. Only ship and 27 | polyfill what's truly necessary! 28 | - Generate and combine TypeScript declarations into a single public-only API representation. 29 | - Generate compact source maps for platform + format based builds. 30 | 31 | ## Requirements 32 | 33 | - Linux, OSX, Windows 34 | - Node 18.12+ 35 | 36 | ## Documentation 37 | 38 | [https://packemon.dev](https://packemon.dev) 39 | -------------------------------------------------------------------------------- /packages/packemon/moon.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | build: 3 | outputs: 4 | - 'mjs' 5 | options: 6 | mergeOutputs: 'replace' 7 | -------------------------------------------------------------------------------- /packages/packemon/src/Config.ts: -------------------------------------------------------------------------------- 1 | import type { Blueprint, Schemas } from '@boost/common'; 2 | import { Configuration } from '@boost/config'; 3 | import type { BuildParams, ConfigFile, ConfigMutator, ConfigMutatorWithBuild } from './types'; 4 | 5 | export class Config extends Configuration { 6 | blueprint({ bool, func }: Schemas): Blueprint { 7 | return { 8 | babelInput: func(), 9 | babelOutput: func(), 10 | rollupInput: func(), 11 | rollupOutput: func(), 12 | swc: bool(), 13 | swcInput: func(), 14 | swcOutput: func(), 15 | }; 16 | } 17 | 18 | override bootstrap() { 19 | this.configureFinder({ 20 | errorIfNoRootFound: false, 21 | extensions: ['js', 'ts'], 22 | }); 23 | 24 | this.addProcessHandler('babelInput', this.wrapMutator); 25 | this.addProcessHandler('babelOutput', this.wrapBuildMutator); 26 | this.addProcessHandler('rollupInput', this.wrapMutator); 27 | this.addProcessHandler('rollupOutput', this.wrapBuildMutator); 28 | } 29 | 30 | wrapMutator(prev?: ConfigMutator, next?: ConfigMutator) { 31 | return (options: T) => { 32 | prev?.(options); 33 | next?.(options); 34 | }; 35 | } 36 | 37 | wrapBuildMutator(prev?: ConfigMutatorWithBuild, next?: ConfigMutatorWithBuild) { 38 | return (options: T, build: BuildParams) => { 39 | prev?.(options, build); 40 | next?.(options, build); 41 | }; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/packemon/src/FileSystem.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import { json } from '@boost/common'; 3 | 4 | export interface FileSystem { 5 | copyFile: (from: string, to: string) => void; 6 | createDirAll: (path: string) => void; 7 | exists: (path: string) => boolean; 8 | readFile: (path: string) => string; 9 | readJson: (path: string) => T; 10 | removeDir: (path: string) => void; 11 | removeFile: (path: string) => void; 12 | writeFile: (path: string, data: string) => void; 13 | writeJson: (path: string, data: unknown) => void; 14 | } 15 | 16 | export const nodeFileSystem: FileSystem = { 17 | copyFile: fs.copyFileSync, 18 | createDirAll: (path) => fs.mkdirSync(path, { recursive: true }), 19 | exists: (path) => fs.existsSync(path), 20 | readFile: (path) => fs.readFileSync(path, 'utf8'), 21 | readJson: (path) => json.parse(nodeFileSystem.readFile(path)), 22 | removeDir: (path) => { 23 | fs.rmSync(path, { recursive: true }); 24 | }, 25 | removeFile: fs.unlinkSync, 26 | writeFile: (path, data) => { 27 | fs.writeFileSync(path, `${data.trim()}\n`, 'utf8'); 28 | }, 29 | writeJson: (path, data) => { 30 | nodeFileSystem.writeFile(path, JSON.stringify(data, null, 2)); 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /packages/packemon/src/babel.ts: -------------------------------------------------------------------------------- 1 | import type { TransformOptions as ConfigStructure } from '@babel/core'; 2 | import { Path, toArray } from '@boost/common'; 3 | import type { FeatureFlags } from './types'; 4 | import { 5 | Artifact, 6 | DEFAULT_SUPPORT, 7 | type Format, 8 | getBabelInputConfig, 9 | getBabelOutputConfig, 10 | Package, 11 | Packemon, 12 | type PackemonPackage, 13 | type Platform, 14 | type Support, 15 | } from '.'; 16 | 17 | const format = (process.env.PACKEMON_FORMAT ?? 'lib') as Format; 18 | const support = (process.env.PACKEMON_SUPPORT ?? DEFAULT_SUPPORT) as Support; 19 | 20 | function getBabelConfig(artifact: Artifact, featureFlags: FeatureFlags): ConfigStructure { 21 | const inputConfig = getBabelInputConfig(artifact, featureFlags, {}); 22 | const outputConfig = getBabelOutputConfig( 23 | artifact.platform, 24 | artifact.support, 25 | artifact.builds[0].format, 26 | featureFlags, 27 | {}, 28 | ); 29 | 30 | return { 31 | // Input must come first 32 | plugins: [...inputConfig.plugins!, ...outputConfig.plugins!], 33 | // Env must come first 34 | presets: [...outputConfig.presets!, ...inputConfig.presets!], 35 | }; 36 | } 37 | 38 | export interface ConfigOptions { 39 | format?: Format; 40 | platform?: Platform; 41 | support?: Support; 42 | } 43 | 44 | export function createConfig(folder: string, options: ConfigOptions = {}): ConfigStructure { 45 | const path = new Path(folder); 46 | const packemon = new Packemon(); 47 | const contents = packemon.fs.readJson(path.append('package.json').path()); 48 | 49 | // Create package and configs 50 | const pkg = new Package(path, contents, packemon.findWorkspaceRoot()); 51 | 52 | if (pkg.json.packemon) { 53 | pkg.setConfigs(toArray(pkg.json.packemon)); 54 | } 55 | 56 | // Determine the lowest platform to support 57 | const platforms = pkg.configs.map((config) => config.platform); 58 | let lowestPlatform: Platform = 'node'; 59 | 60 | // istanbul ignore next 61 | if (platforms.includes('browser')) { 62 | lowestPlatform = 'browser'; 63 | } else if (platforms.includes('electron')) { 64 | lowestPlatform = 'electron'; 65 | } else if (platforms.includes('native')) { 66 | lowestPlatform = 'native'; 67 | } 68 | 69 | // Generate artifact and builds 70 | const artifact = new Artifact(pkg, [{ declaration: false, format: options.format ?? format }]); 71 | artifact.bundle = false; 72 | artifact.platform = options.platform ?? lowestPlatform; 73 | artifact.support = options.support ?? support; 74 | 75 | return getBabelConfig(artifact, pkg.getFeatureFlags()); 76 | } 77 | 78 | export function createRootConfig(options?: ConfigOptions): ConfigStructure { 79 | const config = createConfig(process.cwd(), options); 80 | 81 | return { 82 | ...config, 83 | babelrc: false, 84 | // Support React Native libraries by default 85 | overrides: [ 86 | { 87 | presets: ['@babel/preset-flow'], 88 | test: /node_modules\/((jest-)?react-native|@react-native(-community)?)/iu, 89 | }, 90 | ], 91 | }; 92 | } 93 | -------------------------------------------------------------------------------- /packages/packemon/src/babel/resolve.ts: -------------------------------------------------------------------------------- 1 | // This is in a separate file so that we can mock in tests 2 | 3 | import { createRequire } from 'node:module'; 4 | import path from 'node:path'; 5 | import { fileURLToPath } from 'node:url'; 6 | import doResolve from 'resolve'; 7 | 8 | // Babel resolves plugins against the current working directory 9 | // and will not find globally installed dependencies unless we resolve. 10 | // istanbul ignore next 11 | export function resolve(id: string): string { 12 | let file = import.meta.url; 13 | 14 | // Because of our Babel plugin and Rollup, this may get transpiled differently 15 | // @ts-expect-error Allow this instance check 16 | if (file instanceof URL || (typeof file === 'string' && file.startsWith('file:'))) { 17 | file = fileURLToPath(file); 18 | } 19 | 20 | return doResolve.sync(id, { basedir: path.dirname(file) }); 21 | } 22 | 23 | // Furthermore, some plugins are dependents of Babel and not Packemon, 24 | // so we need to resolve from that context for PnP to work correctly. 25 | const babelRequire = createRequire(resolve('@babel/preset-env/package.json')); 26 | 27 | export function resolveFromBabel(id: string): string { 28 | return babelRequire.resolve(id); 29 | } 30 | -------------------------------------------------------------------------------- /packages/packemon/src/bin.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | import { checkPackageOutdated, Program } from '@boost/cli'; 3 | import { nodeFileSystem } from './FileSystem'; 4 | import { getVersion } from './helpers/getVersion'; 5 | import { 6 | BuildCommand, 7 | BuildWorkspaceCommand, 8 | CleanCommand, 9 | FilesCommand, 10 | InitCommand, 11 | PackCommand, 12 | PackWorkspaceCommand, 13 | ScaffoldCommand, 14 | ValidateCommand, 15 | WatchCommand, 16 | } from '.'; 17 | 18 | let version = '0.0.0-internal'; 19 | 20 | try { 21 | version = getVersion(nodeFileSystem); 22 | } catch { 23 | // Ignore 24 | } 25 | 26 | if (process.argv.includes('--debug')) { 27 | debug.enable('packemon:*'); 28 | } 29 | 30 | async function run() { 31 | const program = new Program({ 32 | bin: 'packemon', 33 | footer: 'Documentation: https://packemon.dev', 34 | name: 'Packemon', 35 | version, 36 | }); 37 | 38 | program 39 | .categories({ 40 | filter: 'Filtering', 41 | }) 42 | .register(new BuildCommand()) 43 | .register(new BuildWorkspaceCommand()) 44 | .register(new CleanCommand()) 45 | .register(new FilesCommand()) 46 | .register(new InitCommand()) 47 | .register(new PackCommand()) 48 | .register(new PackWorkspaceCommand()) 49 | .register(new ScaffoldCommand()) 50 | .register(new ValidateCommand()) 51 | .register(new WatchCommand()); 52 | 53 | if (!process.env.CI && !process.argv.includes('--quiet')) { 54 | program.middleware(checkPackageOutdated('packemon', version)); 55 | } 56 | 57 | await program.runAndExit(process.argv); 58 | } 59 | 60 | // We need to be using modules first! 61 | // eslint-disable-next-line unicorn/prefer-top-level-await 62 | void run(); 63 | -------------------------------------------------------------------------------- /packages/packemon/src/commands/Base.ts: -------------------------------------------------------------------------------- 1 | import { applyStyle, Arg, Command, type GlobalOptions, type PrimitiveType } from '@boost/cli'; 2 | import { figures } from '@boost/terminal'; 3 | import { PackageValidator } from '../PackageValidator'; 4 | import { Packemon } from '../Packemon'; 5 | 6 | export interface CommonOptions { 7 | cwd: string; 8 | skipPrivate: boolean; 9 | } 10 | 11 | export abstract class BaseCommand< 12 | O extends object = {}, 13 | P extends PrimitiveType[] = string[], 14 | > extends Command { 15 | @Arg.String('Current working directory to run in', { category: 'global' }) 16 | cwd: string = ''; 17 | 18 | @Arg.Flag('Enable debug logs', { category: 'global' }) 19 | debug: boolean = false; 20 | 21 | @Arg.String('Only generate specific output formats', { category: 'filter', short: 'f' }) 22 | formats: string = ''; 23 | 24 | @Arg.Flag('Search and load local config files', { category: 'global' }) 25 | loadConfigs: boolean = false; 26 | 27 | @Arg.String('Only target specific platforms', { category: 'filter', short: 'p' }) 28 | platforms: string = ''; 29 | 30 | @Arg.Flag('Display less or no output while running', { category: 'global' }) 31 | quiet: boolean = !!process.env.CI; 32 | 33 | @Arg.Flag('Skip `private` packages', { category: 'filter' }) 34 | skipPrivate: boolean = false; 35 | 36 | protected get packemon() { 37 | return new Packemon(this.cwd || process.cwd()); 38 | } 39 | 40 | protected async getPackage() { 41 | const pkg = await this.packemon.findPackage({ skipPrivate: this.skipPrivate }); 42 | 43 | if (!pkg) { 44 | throw new Error( 45 | `No \`packemon\` configured package found in ${applyStyle( 46 | this.packemon.workingDir.path(), 47 | 'info', 48 | )}!`, 49 | ); 50 | } 51 | 52 | return pkg; 53 | } 54 | 55 | protected renderValidator(validator: PackageValidator) { 56 | if (validator.hasErrors()) { 57 | validator.errors.forEach((error) => { 58 | this.log.error(applyStyle(` ${figures.bullet} ${error}`, 'failure')); 59 | }); 60 | } 61 | 62 | if (validator.hasWarnings()) { 63 | validator.warnings.forEach((warning) => { 64 | this.log.warn(applyStyle(` ${figures.bullet} ${warning}`, 'warning')); 65 | }); 66 | } 67 | 68 | if (validator.hasErrors()) { 69 | this.exit(`Found errors in ${validator.package.getName()} package!`); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /packages/packemon/src/commands/BuildWorkspace.ts: -------------------------------------------------------------------------------- 1 | import { Arg, Config } from '@boost/cli'; 2 | import { PackageGraph } from '@boost/common'; 3 | import { Context, PooledPipeline } from '@boost/pipeline'; 4 | import { Package } from '../Package'; 5 | import { BuildCommand } from './Build'; 6 | 7 | @Config('build-workspace', 'Build all packages across the workspace') 8 | export class BuildWorkspaceCommand extends BuildCommand { 9 | @Arg.String('Filter packages to build', { category: 'filter' }) 10 | filter: string = ''; 11 | 12 | override async run() { 13 | await this.runPipeline(this.build.bind(this)); 14 | } 15 | 16 | protected async runPipeline(run: (pkg: Package) => Promise) { 17 | const packages = await this.packemon.findPackages({ 18 | filter: this.filter, 19 | skipPrivate: this.skipPrivate, 20 | }); 21 | 22 | const map = Object.fromEntries(packages.map((pkg) => [pkg.getName(), pkg])); 23 | const graph = new PackageGraph(packages.map((pkg) => pkg.json)); 24 | 25 | for await (const batch of graph.resolveBatchList()) { 26 | const pipeline = new PooledPipeline(new Context(), undefined, { 27 | concurrency: this.concurrency, 28 | timeout: this.timeout, 29 | }); 30 | 31 | batch.forEach((pkgJson) => { 32 | pipeline.add(pkgJson.name, async () => { 33 | await run(map[pkgJson.name]); 34 | }); 35 | }); 36 | 37 | const { errors } = await pipeline.run(); 38 | 39 | if (errors.length > 0) { 40 | throw errors[0]; 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/packemon/src/commands/Clean.ts: -------------------------------------------------------------------------------- 1 | import { Config } from '@boost/cli'; 2 | import { BaseCommand } from './Base'; 3 | 4 | @Config('clean', 'Clean build artifacts from a package') 5 | export class CleanCommand extends BaseCommand { 6 | async run() { 7 | const pkg = await this.getPackage(); 8 | 9 | await this.packemon.clean(pkg); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/packemon/src/commands/Files.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | 3 | import { Arg, Config } from '@boost/cli'; 4 | import type { FileFormat } from '../components/Files'; 5 | import type { FileTree } from '../components/Files/Tree'; 6 | import { BaseCommand } from './Base'; 7 | 8 | @Config('files', 'List all files that will be distributed within a package') 9 | export class FilesCommand extends BaseCommand { 10 | @Arg.String('Format to display files in', { 11 | choices: ['list', 'tree'], 12 | }) 13 | format: FileFormat = 'tree'; 14 | 15 | async run() { 16 | const pkg = await this.getPackage(); 17 | const files = await pkg.findDistributableFiles(); 18 | const tree = this.convertFilesToTree(files); 19 | 20 | // eslint-disable-next-line import/no-useless-path-segments 21 | const { Files } = await import('../components/Files/index.js'); 22 | 23 | return ; 24 | } 25 | 26 | protected convertFilesToTree(files: string[]): FileTree { 27 | const root: FileTree = { 28 | files: [], 29 | folders: {}, 30 | }; 31 | 32 | const convert = (file: string, tree: FileTree) => { 33 | let slashIndex = file.indexOf('/'); 34 | 35 | if (slashIndex === -1) { 36 | slashIndex = file.indexOf('\\'); 37 | } 38 | 39 | if (slashIndex === -1) { 40 | (tree.files ||= []).push(file); 41 | } else { 42 | const folder = file.slice(0, slashIndex); 43 | 44 | tree.folders ||= {}; 45 | tree.folders[folder] ||= {}; 46 | 47 | convert(file.slice(slashIndex + 1), tree.folders[folder]); 48 | } 49 | }; 50 | 51 | files.forEach((file) => { 52 | convert(file, root); 53 | }); 54 | 55 | return root; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /packages/packemon/src/commands/Init.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-no-bind, react-perf/jsx-no-new-function-as-prop */ 2 | 3 | import { Arg, Config } from '@boost/cli'; 4 | import { DEFAULT_INPUT, DEFAULT_SUPPORT } from '../constants'; 5 | import { Package } from '../Package'; 6 | import type { PackemonPackageConfig } from '../types'; 7 | import { BaseCommand } from './Base'; 8 | 9 | export interface InitOptions { 10 | force: boolean; 11 | skipPrivate: boolean; 12 | } 13 | 14 | @Config('init', 'Initialize and configure a package for Packemon') 15 | export class InitCommand extends BaseCommand { 16 | @Arg.Flag('Override existing configuration') 17 | force: boolean = false; 18 | 19 | async run() { 20 | // Dont use `getPackage` so we dont throw an error 21 | const pkg = await this.packemon.findPackage(); 22 | 23 | if (!pkg) { 24 | this.log.error('No package found in current directory.'); 25 | return undefined; 26 | } 27 | 28 | const name = pkg.getName(); 29 | 30 | if (pkg.json.packemon && !this.force) { 31 | this.log.info(`Package ${name} has already been configured. Pass --force to override.`); 32 | return undefined; 33 | } 34 | 35 | // eslint-disable-next-line import/no-useless-path-segments 36 | const { Init } = await import('../components/Init/index.js'); 37 | 38 | return ( 39 | { 42 | this.writeConfigToPackageJson(pkg, config); 43 | }} 44 | /> 45 | ); 46 | } 47 | 48 | // eslint-disable-next-line complexity 49 | formatConfigObject({ 50 | format, 51 | inputs, 52 | namespace, 53 | platform, 54 | support, 55 | }: PackemonPackageConfig): PackemonPackageConfig { 56 | const config: PackemonPackageConfig = {}; 57 | 58 | if (format) { 59 | if (Array.isArray(format) && format.length === 1) { 60 | [config.format] = format; 61 | } else { 62 | config.format = format; 63 | } 64 | } 65 | 66 | if (inputs && !(Object.keys(inputs).length === 1 && inputs.index === DEFAULT_INPUT)) { 67 | config.inputs = inputs; 68 | } 69 | 70 | if (namespace) { 71 | config.namespace = namespace; 72 | } 73 | 74 | if (platform) { 75 | if (Array.isArray(platform) && platform.length === 1) { 76 | [config.platform] = platform; 77 | } else { 78 | config.platform = platform; 79 | } 80 | } 81 | 82 | if (support && support !== DEFAULT_SUPPORT) { 83 | config.support = support; 84 | } 85 | 86 | return config; 87 | } 88 | 89 | writeConfigToPackageJson(pkg: Package, config: PackemonPackageConfig) { 90 | // eslint-disable-next-line no-param-reassign 91 | pkg.json.packemon = this.formatConfigObject(config); 92 | pkg.syncJson(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /packages/packemon/src/commands/Pack.ts: -------------------------------------------------------------------------------- 1 | import { Config } from '@boost/cli'; 2 | import { BuildCommand } from './Build'; 3 | 4 | @Config('pack', 'Clean, build, and validate a package for distribution') 5 | export class PackCommand extends BuildCommand { 6 | override async run() { 7 | if (!process.env.NODE_ENV) { 8 | process.env.NODE_ENV = 'production'; 9 | } 10 | 11 | await this.pack(await this.getPackage()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/packemon/src/commands/PackWorkspace.ts: -------------------------------------------------------------------------------- 1 | import { Config } from '@boost/cli'; 2 | import { BuildWorkspaceCommand } from './BuildWorkspace'; 3 | 4 | @Config('pack-workspace', 'Clean, build, and validate all packages across the workspace') 5 | export class PackWorkspaceCommand extends BuildWorkspaceCommand { 6 | override async run() { 7 | if (!process.env.NODE_ENV) { 8 | process.env.NODE_ENV = 'production'; 9 | } 10 | 11 | await this.runPipeline(this.pack.bind(this)); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/packemon/src/commands/Validate.ts: -------------------------------------------------------------------------------- 1 | import { Arg, Config } from '@boost/cli'; 2 | import type { ValidateOptions } from '../types'; 3 | import { BaseCommand } from './Base'; 4 | 5 | @Config('validate', 'Validate package metadata and configuration') 6 | export class ValidateCommand extends BaseCommand> { 7 | @Arg.Flag('Check that dependencies have valid versions and constraints') 8 | deps: boolean = true; 9 | 10 | @Arg.Flag('Check that the current runtime satisfies `engines` constraint') 11 | engines: boolean = true; 12 | 13 | @Arg.Flag('Check that `main`, `module`, and other entry points are valid paths') 14 | entries: boolean = true; 15 | 16 | @Arg.Flag('Check that a SPDX license is provided') 17 | license: boolean = true; 18 | 19 | @Arg.Flag('Check that `homepage` and `bugs` links are valid URLs') 20 | links: boolean = true; 21 | 22 | @Arg.Flag('Check that `name`, `version`, `description`, and `keywords` are valid') 23 | meta: boolean = true; 24 | 25 | @Arg.Flag('Check that `author` and `contributors` contain a name and optional URL') 26 | people: boolean = true; 27 | 28 | @Arg.Flag('Check that `repository` exists and is a valid URL') 29 | repo: boolean = true; 30 | 31 | async run() { 32 | const pkg = await this.getPackage(); 33 | 34 | const validator = await this.packemon.validate(pkg, { 35 | deps: this.deps, 36 | engines: this.engines, 37 | entries: this.engines, 38 | license: this.license, 39 | links: this.links, 40 | meta: this.meta, 41 | people: this.people, 42 | quiet: this.quiet, 43 | repo: this.repo, 44 | skipPrivate: this.skipPrivate, 45 | }); 46 | 47 | this.renderValidator(validator); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/packemon/src/components/Files/List.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Text } from 'ink'; 2 | import { Symbol } from './Symbol'; 3 | 4 | export interface ListProps { 5 | items: string[]; 6 | } 7 | 8 | export function List({ items }: ListProps) { 9 | const lastIndex = items.length - 1; 10 | const files: string[] = []; 11 | const folders: string[] = []; 12 | 13 | items 14 | .sort((a, b) => a.localeCompare(b)) 15 | .forEach((item) => { 16 | if (item.includes('/') || item.includes('\\')) { 17 | folders.push(item); 18 | } else { 19 | files.push(item); 20 | } 21 | }); 22 | 23 | return ( 24 | <> 25 | {[...folders, ...files].map((file, index) => ( 26 | 27 | 28 | {file} 29 | 30 | ))} 31 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /packages/packemon/src/components/Files/Symbol.tsx: -------------------------------------------------------------------------------- 1 | import { Style } from '@boost/cli/react'; 2 | 3 | export interface SymbolProps { 4 | depth?: boolean[]; 5 | first: boolean; 6 | last: boolean; 7 | } 8 | 9 | export function Symbol({ depth = [], first, last }: SymbolProps) { 10 | return ( 11 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/packemon/src/components/Files/TreeContext.ts: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from 'react'; 2 | import type { StyleType } from '@boost/cli'; 3 | 4 | interface TreeContextType { 5 | folderStyle?: StyleType; 6 | lastIndex: [string, number]; 7 | } 8 | 9 | export const TreeContext = createContext(undefined); 10 | 11 | export function useTree() { 12 | return useContext(TreeContext); 13 | } 14 | -------------------------------------------------------------------------------- /packages/packemon/src/components/Files/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react-perf/jsx-no-new-array-as-prop */ 2 | 3 | import { Box, Static } from 'ink'; 4 | import { Header } from '@boost/cli/react'; 5 | import { List } from './List'; 6 | import { type FileTree, Tree } from './Tree'; 7 | 8 | export type FileFormat = 'list' | 'tree'; 9 | 10 | export interface FilesProps { 11 | format: FileFormat; 12 | list: string[]; 13 | name: string; 14 | tree: FileTree; 15 | } 16 | 17 | export function Files({ format, list, name, tree }: FilesProps) { 18 | if (format === 'list') { 19 | return ( 20 | 21 | {(root) => ( 22 | 23 |
24 | 25 | 26 | )} 27 | 28 | ); 29 | } 30 | 31 | return ( 32 | 33 | {(root) => ( 34 | 35 |
36 | 37 | 38 | )} 39 | 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /packages/packemon/src/components/Init/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-no-literals */ 2 | 3 | import { useCallback } from 'react'; 4 | import { Box, Text } from 'ink'; 5 | import { Header, Style, useProgram } from '@boost/cli/react'; 6 | import type { PackemonPackageConfig } from '../../types'; 7 | import { PackageForm } from './PackageForm'; 8 | 9 | export type InitPackageConfigs = Record; 10 | 11 | export interface InitProps { 12 | packageName: string; 13 | onComplete: (config: PackemonPackageConfig) => void; 14 | } 15 | 16 | export function Init({ packageName, onComplete }: InitProps) { 17 | const { exit } = useProgram(); 18 | 19 | const handleSubmit = useCallback( 20 | (config: PackemonPackageConfig) => { 21 | try { 22 | onComplete(config); 23 | } catch (error: unknown) { 24 | exit(error as Error); 25 | } finally { 26 | exit(); 27 | } 28 | }, 29 | [exit, onComplete], 30 | ); 31 | 32 | return ( 33 | 34 |
35 | 36 | 37 | 38 | Package to configure: 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /packages/packemon/src/components/Scaffold/TemplateSelect.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useMemo } from 'react'; 2 | import { Select, type SelectOptionLike } from '@boost/cli/react'; 3 | import type { TemplateType } from '../../types'; 4 | 5 | export interface TemplateSelectProps { 6 | defaultTemplate?: TemplateType; 7 | onSelect: (template: TemplateType) => void; 8 | } 9 | 10 | export function TemplateSelect({ defaultTemplate, onSelect }: TemplateSelectProps) { 11 | const options = useMemo[]>( 12 | () => [ 13 | { label: 'Monorepo infrastructure (many packages)', value: 'monorepo' }, 14 | { label: 'Monorepo package', value: 'monorepo-package' }, 15 | { label: 'Polyrepo (single package)', value: 'polyrepo-package' }, 16 | ], 17 | [], 18 | ); 19 | 20 | const validate = useCallback((value: TemplateType) => { 21 | if (!value) { 22 | throw new Error('Please select a template'); 23 | } 24 | }, []); 25 | 26 | return ( 27 | 28 | defaultSelected={defaultTemplate} 29 | label="Template to scaffold?" 30 | options={options} 31 | validate={validate} 32 | onSubmit={onSelect} 33 | /> 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /packages/packemon/src/helpers/compat/convertCjsTypes.ts: -------------------------------------------------------------------------------- 1 | import glob from 'fast-glob'; 2 | import { Path } from '@boost/common'; 3 | import type { FileSystem } from '../../FileSystem'; 4 | 5 | export async function convertCjsTypes(cjsDir: Path, fs: FileSystem) { 6 | const dtsFiles = await glob(['**/*.d.ts', '**/*.d.ts.map'], { 7 | absolute: true, 8 | cwd: cjsDir.path(), 9 | }); 10 | 11 | await Promise.all( 12 | // eslint-disable-next-line @typescript-eslint/require-await 13 | dtsFiles.map(async (dtsFile) => { 14 | const dtsPath = Path.create(dtsFile); 15 | const inName = dtsPath.name(); 16 | const outName = inName.replace('.d.ts', '.d.cts'); 17 | 18 | // Read contents and fix source map paths 19 | let contents = fs.readFile(dtsPath.path()).replace(inName, outName); 20 | 21 | if (dtsFile.endsWith('.map')) { 22 | contents = contents.replace(inName.replace('.map', ''), outName.replace('.map', '')); 23 | } 24 | 25 | // Write the new file 26 | fs.writeFile(dtsPath.parent().append(outName).path(), contents); 27 | 28 | // Delete the old file 29 | if (fs.exists(dtsPath.path())) { 30 | fs.removeFile(dtsPath.path()); 31 | } 32 | }), 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /packages/packemon/src/helpers/getVersion.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { fileURLToPath } from 'node:url'; 3 | import type { PackageStructure } from '@boost/common'; 4 | import type { FileSystem } from '../FileSystem'; 5 | 6 | const PKG_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../package.json'); 7 | 8 | export function getVersion(fs: FileSystem): string { 9 | return fs.readJson(PKG_PATH).version; 10 | } 11 | -------------------------------------------------------------------------------- /packages/packemon/src/helpers/injectDefaultCondition.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | 3 | import type { PackageExportConditions, PackageExports } from '../types'; 4 | 5 | export function injectDefaultCondition(exportMap: PackageExports) { 6 | Object.entries(exportMap).forEach(([path, conditions]) => { 7 | if (typeof conditions !== 'object') { 8 | return; 9 | } 10 | 11 | // Order from most to least down-leveled 12 | if (!conditions.default) { 13 | for (const key of [ 14 | 'browser', 15 | 'react-native', 16 | 'electron', 17 | 'node', 18 | 'require', 19 | ] as PackageExportConditions[]) { 20 | if (conditions[key]) { 21 | conditions.default = conditions[key]; 22 | delete conditions[key]; 23 | break; 24 | } 25 | } 26 | } 27 | 28 | const keys = Object.keys(conditions); 29 | 30 | if (keys.length === 1) { 31 | exportMap[path] = 32 | keys[0] === 'default' ? conditions.default : conditions[keys[0] as PackageExportConditions]; 33 | } else { 34 | exportMap[path] = conditions; 35 | } 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /packages/packemon/src/helpers/loadModule.ts: -------------------------------------------------------------------------------- 1 | import { createRequire } from 'node:module'; 2 | 3 | const modRequire = createRequire(import.meta.url); 4 | 5 | export function loadModule(name: string, message: string): unknown { 6 | try { 7 | return modRequire(name); 8 | } catch { 9 | throw new Error(`${message} Please install with \`yarn add --dev ${name}\`.`); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/packemon/src/helpers/loadTsconfigJson.ts: -------------------------------------------------------------------------------- 1 | import { Path, type PortablePath } from '@boost/common'; 2 | import type { FileSystem } from '../FileSystem'; 3 | import type { TSConfigStructure } from '../types'; 4 | import { loadModule } from './loadModule'; 5 | 6 | const CACHE = new Map(); 7 | 8 | export function loadTsconfigJson( 9 | path: PortablePath, 10 | fs: FileSystem, 11 | ): TSConfigStructure | undefined { 12 | const tsconfigJsonPath = Path.create(path); 13 | const tsconfig = CACHE.get(tsconfigJsonPath); 14 | 15 | if (tsconfig) { 16 | return tsconfig; 17 | } 18 | 19 | if (!tsconfigJsonPath.exists()) { 20 | return undefined; 21 | } 22 | 23 | const ts = loadModule( 24 | 'typescript', 25 | 'TypeScript is required for config loading.', 26 | ) as typeof import('typescript'); 27 | 28 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 29 | const { config, error } = ts.readConfigFile(tsconfigJsonPath.path(), fs.readFile); 30 | 31 | const host = { 32 | getCanonicalFileName: (fileName: string) => fileName, 33 | getCurrentDirectory: () => ts.sys.getCurrentDirectory(), 34 | getNewLine: () => ts.sys.newLine, 35 | }; 36 | 37 | // istanbul ignore next 38 | if (error) { 39 | throw new Error(ts.formatDiagnostic(error, host)); 40 | } 41 | 42 | const result = ts.parseJsonConfigFileContent( 43 | config, 44 | ts.sys, 45 | tsconfigJsonPath.parent().path(), 46 | {}, 47 | tsconfigJsonPath.path(), 48 | ); 49 | 50 | // istanbul ignore next 51 | if (result.errors.length > 0) { 52 | throw new Error(ts.formatDiagnostics(result.errors, host)); 53 | } 54 | 55 | CACHE.set(tsconfigJsonPath, result); 56 | 57 | return result; 58 | } 59 | -------------------------------------------------------------------------------- /packages/packemon/src/helpers/matchesPattern.ts: -------------------------------------------------------------------------------- 1 | import micromatch from 'micromatch'; 2 | 3 | export function matchesPattern(value: string, pattern: string) { 4 | const patterns = pattern.split(','); 5 | 6 | return micromatch.isMatch(value, patterns) || patterns.includes(value); 7 | } 8 | -------------------------------------------------------------------------------- /packages/packemon/src/helpers/mergeExports.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | 3 | import type { PackageExportConditions, PackageExportPaths } from '../types'; 4 | 5 | export function mergeExports( 6 | prev: PackageExportPaths | string, 7 | base: PackageExportPaths | string, 8 | ): PackageExportPaths | undefined { 9 | const next = typeof base === 'string' ? { default: base } : base; 10 | 11 | if (typeof prev === 'string') { 12 | return next; 13 | } 14 | 15 | Object.entries(next).forEach(([origKey, nextValue]) => { 16 | const key = origKey as PackageExportConditions; 17 | const prevValue = prev[key]; 18 | 19 | if (!prevValue) { 20 | prev[key] = nextValue; 21 | } else if (nextValue) { 22 | prev[key] = mergeExports(prevValue, nextValue); 23 | } 24 | }); 25 | 26 | return prev; 27 | } 28 | -------------------------------------------------------------------------------- /packages/packemon/src/helpers/removeSourcePath.ts: -------------------------------------------------------------------------------- 1 | export function removeSourcePath(file: string): string { 2 | return file.replace('src/', '').replace(/\.[a-z]{2,3}$/, ''); 3 | } 4 | -------------------------------------------------------------------------------- /packages/packemon/src/helpers/shouldKeepDynamicImport.ts: -------------------------------------------------------------------------------- 1 | import type { Format, Platform } from '../types'; 2 | 3 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#browser_compatibility 4 | export function shouldKeepDynamicImport(platform: Platform, format: Format): boolean { 5 | if (format === 'umd') { 6 | return false; 7 | } 8 | 9 | switch (platform) { 10 | case 'node': 11 | // >= v13.2 12 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import#browser_compatibility 13 | return true; 14 | case 'electron': 15 | case 'browser': 16 | // >= 2019 17 | // https://caniuse.com/es6-module-dynamic-import 18 | return true; 19 | case 'native': 20 | // >= RN 0.72 21 | // https://metrobundler.dev/docs/module-api/#import-dynamic-import 22 | return true; 23 | default: 24 | return false; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/packemon/src/helpers/sortExportConditions.ts: -------------------------------------------------------------------------------- 1 | import type { PackageExportPaths } from '../types'; 2 | 3 | // https://nodejs.org/api/packages.html#conditional-exports 4 | const WEIGHTS = { 5 | types: 10, // Types must always be first 6 | solid: 15, 7 | misc: 20, 8 | 'node-addons': 30, 9 | node: 31, 10 | import: 40, 11 | require: 50, 12 | default: 100, // Default must be last 13 | }; 14 | 15 | export function flattenExportConditions(paths: PackageExportPaths): PackageExportPaths | string { 16 | const map: PackageExportPaths = {}; 17 | let count = 0; 18 | 19 | Object.entries(paths).forEach(([path, condition]) => { 20 | // Remove undefined and empty values 21 | if (!condition) { 22 | return; 23 | } 24 | 25 | const key = path as keyof PackageExportPaths; 26 | 27 | map[key] = typeof condition === 'string' ? condition : flattenExportConditions(condition); 28 | 29 | count += 1; 30 | }); 31 | 32 | if (count === 1) { 33 | if (map.default) { 34 | return map.default; 35 | } 36 | 37 | if (map.require) { 38 | return map.require; 39 | } 40 | } 41 | 42 | return map; 43 | } 44 | 45 | export function sortExportConditions( 46 | paths: T, 47 | ): T { 48 | if (!paths || typeof paths === 'string') { 49 | return paths; 50 | } 51 | 52 | const pathsList: { weight: number; key: string; value: PackageExportPaths | string }[] = []; 53 | 54 | Object.entries(paths).forEach(([key, value]) => { 55 | if (!value) { 56 | return; 57 | } 58 | 59 | pathsList.push({ 60 | key, 61 | value: sortExportConditions(value), 62 | weight: key in WEIGHTS ? WEIGHTS[key as 'misc'] : WEIGHTS.misc, 63 | }); 64 | }); 65 | 66 | pathsList.sort((a, d) => { 67 | const diff = a.weight - d.weight; 68 | 69 | return diff === 0 ? a.key.localeCompare(d.key) : diff; 70 | }); 71 | 72 | const map = Object.fromEntries( 73 | pathsList.map((path) => [ 74 | path.key, 75 | typeof path.value === 'string' ? path.value : flattenExportConditions(path.value), 76 | ]), 77 | ) as PackageExportPaths; 78 | 79 | return map as T; 80 | } 81 | -------------------------------------------------------------------------------- /packages/packemon/src/helpers/sortExports.ts: -------------------------------------------------------------------------------- 1 | import type { PackageExports } from '../types'; 2 | import { sortExportConditions } from './sortExportConditions'; 3 | 4 | const WEIGHTS: Record = { 5 | './package.json': 0, // First 6 | '.': 100, // Last 7 | }; 8 | 9 | export function sortExports(exportMap: PackageExports): PackageExports { 10 | const paths = Object.keys(exportMap); 11 | 12 | paths.sort((a, d) => { 13 | const diff = (WEIGHTS[a] ?? 10) - (WEIGHTS[d] ?? 10); 14 | 15 | return diff === 0 ? d.length - a.length : diff; 16 | }); 17 | 18 | return Object.fromEntries( 19 | paths.map((path) => [ 20 | path, 21 | exportMap[path] === undefined ? undefined : sortExportConditions(exportMap[path]), 22 | ]), 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /packages/packemon/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @copyright 2020, Miles Johnson 3 | * @license https://opensource.org/licenses/MIT 4 | */ 5 | 6 | export * from './Artifact'; 7 | export * from './babel/config'; 8 | export * from './commands/Base'; 9 | export * from './commands/Build'; 10 | export * from './commands/BuildWorkspace'; 11 | export * from './commands/Clean'; 12 | export * from './commands/Files'; 13 | export * from './commands/Init'; 14 | export * from './commands/Pack'; 15 | export * from './commands/PackWorkspace'; 16 | export * from './commands/Scaffold'; 17 | export * from './commands/Validate'; 18 | export * from './commands/Watch'; 19 | export * from './constants'; 20 | export type { FileSystem } from './FileSystem'; 21 | export * from './Package'; 22 | export * from './PackageValidator'; 23 | export * from './Packemon'; 24 | export * from './rollup/config'; 25 | export * from './swc/config'; 26 | export type * from './types'; 27 | -------------------------------------------------------------------------------- /packages/packemon/src/rollup/plugins/addBinShebang.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ 2 | 3 | import type { OutputPlugin } from 'rollup'; 4 | 5 | export function addBinShebang(): OutputPlugin { 6 | return { 7 | name: 'packemon-add-bin-shebang', 8 | 9 | generateBundle(options, bundle) { 10 | Object.entries(bundle).forEach(([outputPath, chunk]) => { 11 | if ( 12 | (outputPath.match(/bin\.(js|cjs|mjs)$/) || 13 | outputPath.includes('bin/') || 14 | outputPath.includes('bins/')) && 15 | chunk.type === 'chunk' 16 | ) { 17 | // eslint-disable-next-line no-param-reassign 18 | chunk.code = `#!/usr/bin/env node\n${chunk.code}`; 19 | } 20 | }); 21 | }, 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /packages/packemon/src/rollup/plugins/preserveDynamicImport.ts: -------------------------------------------------------------------------------- 1 | import type { OutputPlugin } from 'rollup'; 2 | import { shouldKeepDynamicImport } from '../../helpers/shouldKeepDynamicImport'; 3 | import type { Format, Platform } from '../../types'; 4 | 5 | export function preserveDynamicImport(platform: Platform, format: Format): OutputPlugin { 6 | const preserve = shouldKeepDynamicImport(platform, format); 7 | 8 | return { 9 | name: 'packemon-preserve-dynamic-import', 10 | 11 | renderDynamicImport() { 12 | if (preserve) { 13 | return { 14 | left: 'import(', 15 | right: ')', 16 | }; 17 | } 18 | 19 | return null; 20 | }, 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /packages/packemon/src/rollup/plugins/swc.ts: -------------------------------------------------------------------------------- 1 | import type { OutputPlugin, Plugin } from 'rollup'; 2 | import { type Options, transform } from '@swc/core'; 3 | 4 | export function swcInput(config: Partial): Plugin { 5 | return { 6 | name: 'packemon-swc-input', 7 | 8 | async transform(code, filename) { 9 | return transform(code, { 10 | ...config, 11 | filename, 12 | sourceMaps: true, 13 | }); 14 | }, 15 | }; 16 | } 17 | 18 | export function swcOutput(config: Partial): OutputPlugin { 19 | return { 20 | name: 'packemon-swc-output', 21 | 22 | async renderChunk(code) { 23 | return transform(code, { 24 | ...config, 25 | sourceMaps: true, 26 | }); 27 | }, 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /packages/packemon/templates/base/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | // https://www.npmjs.com/package/eslint-config-moon 2 | module.exports = { 3 | root: true, 4 | extends: [ 5 | 'moon', 6 | 'moon/node', 7 | // Uncomment when targeting browsers 8 | // 'moon/browser', 9 | // Uncomment if using React 10 | // 'moon/react', 11 | // Uncomment if using Solid 12 | // 'moon/solid', 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /packages/packemon/templates/base/.gitignore: -------------------------------------------------------------------------------- 1 | .eslintcache 2 | 3 | # Build folders 4 | coverage/ 5 | build/ 6 | cjs/ 7 | dts/ 8 | esm/ 9 | lib/ 10 | mjs/ 11 | umd/ 12 | 13 | # Artifacts 14 | *.map 15 | *.min.js 16 | *.tsbuildinfo 17 | -------------------------------------------------------------------------------- /packages/packemon/templates/base/.prettierignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | 3 | # Build folders 4 | coverage/ 5 | build/ 6 | cjs/ 7 | dts/ 8 | esm/ 9 | lib/ 10 | mjs/ 11 | umd/ 12 | -------------------------------------------------------------------------------- /packages/packemon/templates/base/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | -------------------------------------------------------------------------------- /packages/packemon/templates/base/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/packemon/templates/base/babel.config.js: -------------------------------------------------------------------------------- 1 | // https://www.npmjs.com/package/babel-preset-moon 2 | module.exports = { 3 | presets: ['moon'], 4 | }; 5 | -------------------------------------------------------------------------------- /packages/packemon/templates/base/prettier.config.js: -------------------------------------------------------------------------------- 1 | // https://www.npmjs.com/package/prettier-config-moon 2 | module.exports = 'prettier-config-moon'; 3 | -------------------------------------------------------------------------------- /packages/packemon/templates/monorepo-package/README.md: -------------------------------------------------------------------------------- 1 | # 2 | -------------------------------------------------------------------------------- /packages/packemon/templates/monorepo-package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "repository": { 3 | "type": "git", 4 | "url": "", 5 | "directory": "" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/packemon/templates/monorepo-package/tsconfig.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.options.json", 3 | "compilerOptions": { 4 | "outDir": "esm", 5 | "rootDir": "src" 6 | }, 7 | "include": [ 8 | "src/**/*", 9 | "types/**/*", 10 | "../../types/**/*" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/packemon/templates/monorepo-package/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.options.json", 3 | "compilerOptions": { 4 | "emitDeclarationOnly": false, 5 | "noEmit": true 6 | }, 7 | "include": [ 8 | "src/**/*", 9 | "tests/**/*", 10 | "types/**/*", 11 | "../../types/**/*" 12 | ], 13 | "references": [ 14 | { 15 | "path": "tsconfig.esm.json" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/packemon/templates/monorepo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "", 4 | "infra": "monorepo", 5 | "scripts": { 6 | "build": "packemon build-workspace", 7 | "check": "yarn run type && yarn run test && yarn run lint", 8 | "coverage": "yarn run test --coverage", 9 | "format": "prettier --write ./", 10 | "lint": "eslint --cache --fix ./", 11 | "pack": "packemon pack-workspace --addEngines --declaration", 12 | "prelease": "yarn run pack && yarn run check", 13 | "release": "echo 'Customize your own release script!'", 14 | "test": "vitest", 15 | "type": "tsc --build" 16 | }, 17 | "workspaces": [ 18 | "/*" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/packemon/templates/monorepo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.options.json", 3 | "files": [], 4 | "references": [] 5 | } 6 | -------------------------------------------------------------------------------- /packages/packemon/templates/monorepo/tsconfig.options.json: -------------------------------------------------------------------------------- 1 | // https://www.npmjs.com/package/tsconfig-moon 2 | { 3 | "extends": "tsconfig-moon/tsconfig.projects.json", 4 | // Uncomment if using React 5 | // "compilerOptions": { 6 | // "lib": ["dom"], 7 | // "jsx": "react" 8 | // } 9 | // Uncomment if using Solid 10 | // "compilerOptions": { 11 | // "lib": ["dom"], 12 | // "jsx": "preserve", 13 | // "jsxImportSource": "solid-js" 14 | // } 15 | } 16 | -------------------------------------------------------------------------------- /packages/packemon/templates/package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "version": "0.0.0", 4 | "description": "", 5 | "keywords": [], 6 | "author": "", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "" 11 | }, 12 | "packemon": { 13 | "platform": "browser", 14 | "format": "esm" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/packemon/templates/package/src/index.ts: -------------------------------------------------------------------------------- 1 | export function add(a: number, b: number): number { 2 | return a + b; 3 | } 4 | -------------------------------------------------------------------------------- /packages/packemon/templates/package/tests/index.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { add } from '../src'; 3 | 4 | describe('add()', () => { 5 | it('should add numbers', () => { 6 | expect(add(5, 5)).toBe(10); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /packages/packemon/templates/polyrepo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "infra": "polyrepo", 4 | "scripts": { 5 | "build": "packemon build", 6 | "check": "yarn run type && yarn run test && yarn run lint", 7 | "clean": "packemon clean", 8 | "coverage": "yarn run test --coverage", 9 | "format": "prettier --write ./src ./tests", 10 | "lint": "eslint --cache --fix ./src ./tests", 11 | "pack": "packemon pack --addEngines --declaration", 12 | "prelease": "yarn run pack && yarn run check", 13 | "release": "echo 'Customize your own release script!'", 14 | "test": "vitest", 15 | "type": "tsc && tsc -p ./tests", 16 | "validate": "packemon validate", 17 | "watch": "packemon watch" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/packemon/templates/polyrepo/tsconfig.esm.json: -------------------------------------------------------------------------------- 1 | // https://www.npmjs.com/package/tsconfig-moon 2 | { 3 | "extends": "tsconfig-moon/tsconfig.json", 4 | "compilerOptions": { 5 | "declaration": true, 6 | "emitDeclarationOnly": true, 7 | "outDir": "esm", 8 | "rootDir": "src" 9 | }, 10 | "include": ["src/**/*", "types/**/*"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/packemon/templates/polyrepo/tsconfig.json: -------------------------------------------------------------------------------- 1 | // https://www.npmjs.com/package/tsconfig-moon 2 | { 3 | "extends": "tsconfig-moon/tsconfig.json", 4 | "compilerOptions": { 5 | "noEmit": true 6 | }, 7 | "include": ["src/**/*", "tests/**/*", "types/**/*"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/packemon/tests/assets.test.ts: -------------------------------------------------------------------------------- 1 | import type { InputPluginOption } from 'rollup'; 2 | import { describe, it, vi } from 'vitest'; 3 | import { Path } from '@boost/common'; 4 | import { vanillaExtractPlugin } from '@vanilla-extract/rollup-plugin'; 5 | import { Artifact } from '../src'; 6 | import { getFixturePath, loadPackageAtPath, snapshotPackageBuildOutputs } from './helpers'; 7 | 8 | vi.setConfig({ testTimeout: 30_000 }); 9 | 10 | describe('vanilla extract', () => { 11 | it('no bundle + compile', async () => { 12 | const root = new Path(getFixturePath('project-assets-vanilla')); 13 | const pkg = loadPackageAtPath(root); 14 | 15 | const index = new Artifact(pkg, [{ format: 'lib' }, { format: 'esm' }]); 16 | index.bundle = false; 17 | index.platform = 'browser'; 18 | index.support = 'stable'; 19 | index.inputs = { index: 'src/index.tsx' }; 20 | 21 | pkg.artifacts.push(index); 22 | 23 | await pkg.build( 24 | { addEntries: false }, 25 | { 26 | rollupInput(config) { 27 | (config.plugins as InputPluginOption[]).push( 28 | vanillaExtractPlugin({ 29 | cwd: root.path(), 30 | }), 31 | ); 32 | }, 33 | }, 34 | ); 35 | 36 | snapshotPackageBuildOutputs(pkg); 37 | }); 38 | 39 | it('bundle + compile', async () => { 40 | const root = new Path(getFixturePath('project-assets-vanilla')); 41 | const pkg = loadPackageAtPath(root); 42 | 43 | const index = new Artifact(pkg, [{ format: 'lib' }, { format: 'esm' }]); 44 | index.bundle = true; 45 | index.platform = 'browser'; 46 | index.support = 'stable'; 47 | index.inputs = { index: 'src/index.tsx' }; 48 | 49 | pkg.artifacts.push(index); 50 | 51 | await pkg.build( 52 | { addEntries: false }, 53 | { 54 | rollupInput(config) { 55 | (config.plugins as InputPluginOption[]).push( 56 | vanillaExtractPlugin({ 57 | cwd: root.path(), 58 | }), 59 | ); 60 | }, 61 | }, 62 | ); 63 | 64 | snapshotPackageBuildOutputs(pkg); 65 | }); 66 | 67 | it('no bundle + no compile', async () => { 68 | const root = new Path(getFixturePath('project-assets-vanilla')); 69 | const pkg = loadPackageAtPath(root); 70 | 71 | const index = new Artifact(pkg, [{ format: 'lib' }, { format: 'esm' }]); 72 | index.bundle = false; 73 | index.platform = 'browser'; 74 | index.support = 'stable'; 75 | index.inputs = { index: 'src/index.tsx' }; 76 | 77 | pkg.artifacts.push(index); 78 | 79 | await pkg.build({ addEntries: false }, {}); 80 | 81 | snapshotPackageBuildOutputs(pkg); 82 | }); 83 | 84 | it('bundle + no compile', async () => { 85 | const root = new Path(getFixturePath('project-assets-vanilla')); 86 | const pkg = loadPackageAtPath(root); 87 | 88 | const index = new Artifact(pkg, [{ format: 'lib' }, { format: 'esm' }]); 89 | index.bundle = true; 90 | index.platform = 'browser'; 91 | index.support = 'stable'; 92 | index.inputs = { index: 'src/index.tsx' }; 93 | 94 | pkg.artifacts.push(index); 95 | 96 | await pkg.build({ addEntries: false }, {}); 97 | 98 | snapshotPackageBuildOutputs(pkg); 99 | }); 100 | }); 101 | -------------------------------------------------------------------------------- /packages/packemon/tests/examples/assetImports.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest'; 2 | import { testExampleOutput } from '../helpers'; 3 | 4 | describe('Assets', () => { 5 | testExampleOutput('asset-imports.ts', 'babel'); 6 | testExampleOutput('asset-imports.ts', 'swc'); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/packemon/tests/examples/asyncAwait.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest'; 2 | import { testExampleOutput } from '../helpers'; 3 | 4 | describe('Async/await', () => { 5 | testExampleOutput('async-await.ts', 'babel'); 6 | testExampleOutput('async-await.ts', 'swc'); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/packemon/tests/examples/cjsEsmInterop.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest'; 2 | import { testExampleOutput } from '../helpers'; 3 | 4 | describe('CJS/ESM interop', () => { 5 | testExampleOutput('cjs-esm-interop.ts', 'babel'); 6 | // Not supported since swc doesnt have plugins 7 | // testExampleOutput('cjs-esm-interop.ts', 'swc'); 8 | }); 9 | -------------------------------------------------------------------------------- /packages/packemon/tests/examples/cjsMjsWrapper.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest'; 2 | import { testExampleOutput } from '../helpers'; 3 | 4 | describe('CJS -> MJS wrapper', () => { 5 | testExampleOutput('cjs-mjs-wrapper.ts', 'babel'); 6 | testExampleOutput('cjs-mjs-wrapper.ts', 'swc'); 7 | }); 8 | 9 | describe('CJS -> MJS wrapper (externals)', () => { 10 | testExampleOutput('cjs-mjs-wrapper-externals.ts', 'babel'); 11 | testExampleOutput('cjs-mjs-wrapper-externals.ts', 'swc'); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/packemon/tests/examples/dynamicImports.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest'; 2 | import { testExampleOutput } from '../helpers'; 3 | 4 | describe('Dynamic imports', () => { 5 | testExampleOutput('dynamic-imports.ts', 'babel'); 6 | testExampleOutput('dynamic-imports.ts', 'swc'); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/packemon/tests/examples/externals.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest'; 2 | import { testExampleOutput } from '../helpers'; 3 | 4 | // Dont need to test swc here 5 | 6 | describe('Rollup externals', () => { 7 | testExampleOutput('externals.ts', 'babel', { externals: ['@packemon/foo', '@packemon/bar'] }); 8 | }); 9 | 10 | describe('Rollup externals (regex)', () => { 11 | testExampleOutput('externals.ts', 'babel', { externals: [String.raw`@packemon/\*`] }); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/packemon/tests/examples/generators.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest'; 2 | import { testExampleOutput } from '../helpers'; 3 | 4 | describe('Generators', () => { 5 | testExampleOutput('generators.ts', 'babel'); 6 | testExampleOutput('generators.ts', 'swc'); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/packemon/tests/examples/importAttributes.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest'; 2 | import { testExampleOutput } from '../helpers'; 3 | 4 | describe('Import attributes', () => { 5 | testExampleOutput('import-attributes.ts', 'babel'); 6 | testExampleOutput('import-attributes.ts', 'swc'); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/packemon/tests/examples/jsonImports.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest'; 2 | import { testExampleOutput } from '../helpers'; 3 | 4 | describe('JSON imports', () => { 5 | testExampleOutput('json-imports.ts', 'babel'); 6 | testExampleOutput('json-imports.ts', 'swc'); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/packemon/tests/examples/namespaces.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest'; 2 | import { testExampleOutput } from '../helpers'; 3 | 4 | describe('Namespaces', () => { 5 | testExampleOutput('namespaces.ts', 'babel'); 6 | testExampleOutput('namespaces.ts', 'swc'); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/packemon/tests/examples/newSyntax.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest'; 2 | import { testExampleOutput } from '../helpers'; 3 | 4 | describe('New syntax', () => { 5 | testExampleOutput('new-syntax.ts', 'babel'); 6 | testExampleOutput('new-syntax.ts', 'swc'); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/packemon/tests/examples/nodePolyfills.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest'; 2 | import { testExampleOutput } from '../helpers'; 3 | 4 | // This is currently buggy! 5 | describe.skip('Node polyfills', () => { 6 | testExampleOutput('node-polyfills.ts', 'babel'); 7 | testExampleOutput('node-polyfills.ts', 'swc'); 8 | }); 9 | -------------------------------------------------------------------------------- /packages/packemon/tests/examples/react.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest'; 2 | import { testExampleOutput } from '../helpers'; 3 | 4 | // Automatic only 5 | describe('React/JSX', () => { 6 | testExampleOutput('react.tsx', 'babel'); 7 | testExampleOutput('react.tsx', 'swc'); 8 | }); 9 | -------------------------------------------------------------------------------- /packages/packemon/tests/examples/solid.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'vitest'; 2 | import { Path } from '@boost/common'; 3 | import { getFixturePath, testExampleOutput } from '../helpers'; 4 | 5 | // Automatic only 6 | describe('Solid/JSX', () => { 7 | const root = new Path(getFixturePath('example-solid')); 8 | 9 | testExampleOutput('solid.tsx', 'babel', undefined, root); 10 | testExampleOutput('solid.tsx', 'swc', undefined, root); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/packemon/tests/helpers/loadModule.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { loadModule } from '../../src/helpers/loadModule'; 3 | 4 | describe('loadModule()', () => { 5 | it('doesnt error if module exists', () => { 6 | expect(() => loadModule('typescript', '')).not.toThrow(); 7 | }); 8 | 9 | it('errors if module does not exist', () => { 10 | expect(() => loadModule('unknown-module', 'Fake module!')).toThrow( 11 | 'Fake module! Please install with `yarn add --dev unknown-module`.', 12 | ); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/packemon/tests/helpers/sortExportConditions.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { sortExportConditions } from '../../src/helpers/sortExportConditions'; 3 | 4 | describe('sortExportConditions()', () => { 5 | it('sorts in the correct order', () => { 6 | const map = sortExportConditions({ 7 | default: 'index.js', 8 | script: 'index.js', 9 | import: 'index.js', 10 | node: 'index.js', 11 | require: 'index.js', 12 | types: 'index.js', 13 | browser: 'index.js', 14 | }); 15 | 16 | expect(Object.keys(map)).toStrictEqual([ 17 | 'types', 18 | 'browser', 19 | 'script', 20 | 'node', 21 | 'import', 22 | 'require', 23 | 'default', 24 | ]); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /packages/packemon/tests/rollup/plugins/__fixtures__/bin.ts: -------------------------------------------------------------------------------- 1 | export function run() { 2 | console.log('!'); 3 | } 4 | -------------------------------------------------------------------------------- /packages/packemon/tests/rollup/plugins/__fixtures__/src/components/AnotherComponent/anotherEntry.mjs: -------------------------------------------------------------------------------- 1 | export const ae = 1; 2 | -------------------------------------------------------------------------------- /packages/packemon/tests/rollup/plugins/__fixtures__/src/components/MyComponent/MySubComponent/bar.mjs: -------------------------------------------------------------------------------- 1 | import svg from './test.svg'; 2 | 3 | export const newSvg = `new: ${svg}`; 4 | -------------------------------------------------------------------------------- /packages/packemon/tests/rollup/plugins/__fixtures__/src/components/MyComponent/MySubComponent/test.svg: -------------------------------------------------------------------------------- 1 | export default ''; 2 | -------------------------------------------------------------------------------- /packages/packemon/tests/rollup/plugins/__fixtures__/src/components/MyComponent/entry.mjs: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line unicorn/prefer-module 2 | import { newSvg } from './MySubComponent/bar.mjs'; 3 | 4 | console.log(newSvg); 5 | export const foo = 124; 6 | -------------------------------------------------------------------------------- /packages/packemon/tests/rollup/plugins/addBinShebang.test.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { type InputOption, type OutputOptions, rollup } from 'rollup'; 3 | import { describe, expect, it } from 'vitest'; 4 | import { addBinShebang } from '../../../src/rollup/plugins/addBinShebang'; 5 | 6 | async function transform(input: InputOption, options: OutputOptions = {}): Promise { 7 | const bundle = await rollup({ 8 | input, 9 | }); 10 | 11 | const { output } = await bundle.generate({ 12 | dir: 'out', 13 | format: 'cjs', 14 | plugins: [addBinShebang()], 15 | ...options, 16 | }); 17 | 18 | return output[0].code || ''; 19 | } 20 | 21 | describe('addBinShebang()', () => { 22 | // eslint-disable-next-line unicorn/prefer-module 23 | const fixturePath = path.join(__dirname, '__fixtures__/bin.ts'); 24 | 25 | it('doesnt add shebang if filename doesnt contain bin', async () => { 26 | const code = await transform({ index: fixturePath }); 27 | 28 | expect(code).not.toContain('#!/usr/bin/env node\n'); 29 | }); 30 | 31 | it('adds shebang to "bin.js"', async () => { 32 | const code = await transform({ bin: fixturePath }); 33 | 34 | expect(code).toContain('#!/usr/bin/env node\n'); 35 | }); 36 | 37 | it('adds shebang to "bin.cjs"', async () => { 38 | const code = await transform( 39 | { bin: fixturePath }, 40 | { 41 | entryFileNames: '[name].cjs', 42 | }, 43 | ); 44 | 45 | expect(code).toContain('#!/usr/bin/env node\n'); 46 | }); 47 | 48 | it('adds shebang to "bin.mjs"', async () => { 49 | const code = await transform( 50 | { bin: fixturePath }, 51 | { 52 | entryFileNames: '[name].mjs', 53 | }, 54 | ); 55 | 56 | expect(code).toContain('#!/usr/bin/env node\n'); 57 | }); 58 | 59 | it('doesnt add shebang to "bin.ts" (invalid)', async () => { 60 | const code = await transform( 61 | { index: fixturePath }, 62 | { 63 | entryFileNames: '[name].ts', 64 | }, 65 | ); 66 | 67 | expect(code).not.toContain('#!/usr/bin/env node\n'); 68 | }); 69 | 70 | it('adds shebang when bin is in a subfolder', async () => { 71 | const code = await transform({ 'sub/bin': fixturePath }); 72 | 73 | expect(code).toContain('#!/usr/bin/env node\n'); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /packages/packemon/tests/rollup/plugins/copyAndRefAssets.test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable unicorn/prefer-module */ 2 | 3 | import path from 'node:path'; 4 | import { rollup } from 'rollup'; 5 | import { describe, expect, it, vi } from 'vitest'; 6 | import { VirtualPath } from '@boost/common'; 7 | import commonjs from '@rollup/plugin-commonjs'; 8 | import { copyAndRefAssets } from '../../../src/rollup/plugins/copyAndRefAssets'; 9 | import { createStubbedFileSystem } from '../../helpers'; 10 | 11 | describe('copyAndRefAssets()', () => { 12 | const fixturePath1 = path.join(__dirname, '__fixtures__/src/components/MyComponent/entry.mjs'); 13 | const fixturePath2 = path.join( 14 | __dirname, 15 | '__fixtures__/src/components/AnotherComponent/anotherEntry.mjs', 16 | ); 17 | 18 | it('should fix overlapping paths', async () => { 19 | const assetsToCopy = {}; 20 | const fs = createStubbedFileSystem(); 21 | 22 | const copyRefPlugin = copyAndRefAssets( 23 | { 24 | dir: '/root/fakeAssets', 25 | fs, 26 | root: path.join(__dirname, '__fixtures__'), 27 | }, 28 | assetsToCopy, 29 | ); 30 | copyRefPlugin.buildStart = vi.fn(); 31 | copyRefPlugin.generateBundle = vi.fn(); 32 | 33 | const bundle = await rollup({ 34 | input: { another: fixturePath1, myComponent: fixturePath2 }, 35 | // external: (id) => id.endsWith('.svg'), // treat .svg files as external 36 | plugins: [commonjs(), copyRefPlugin], 37 | }); 38 | 39 | await bundle.generate({ 40 | dir: 'out', 41 | format: 'cjs', 42 | }); 43 | 44 | const result = new VirtualPath('/root/fakeAssets/test-7751cc4a.svg'); 45 | result.path(); 46 | 47 | expect(assetsToCopy).toEqual({ 48 | [path.join(__dirname, '__fixtures__/src/components/MyComponent/MySubComponent/test.svg')]: 49 | result, 50 | }); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /packages/packemon/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.options.json", 3 | "compilerOptions": { 4 | "outDir": "../../.moon/cache/types/packages/packemon" 5 | }, 6 | "include": [ 7 | "src/**/*", 8 | "tests/**/*", 9 | "types/**/*", 10 | "../../types/**/*" 11 | ], 12 | "references": [ 13 | { 14 | "path": "../babel-plugin-cjs-esm-interop" 15 | }, 16 | { 17 | "path": "../babel-plugin-conditional-invariant" 18 | }, 19 | { 20 | "path": "../babel-plugin-env-constants" 21 | }, 22 | { 23 | "path": "./tsconfig.mjs.json" 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /packages/packemon/tsconfig.mjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.options.json", 3 | "compilerOptions": { 4 | "outDir": "mjs", 5 | "rootDir": "src" 6 | }, 7 | "include": [ 8 | "src/**/*" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/packemon/types/ink-progress-bar.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'ink-progress-bar' { 2 | import type { FC } from 'react'; 3 | 4 | interface Props { 5 | character?: string; 6 | percent: number; 7 | left?: number; 8 | right?: number; 9 | } 10 | 11 | const ProgressBar: FC; 12 | 13 | export default ProgressBar; 14 | } 15 | -------------------------------------------------------------------------------- /packages/packemon/types/spdx-license-list.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'spdx-license-list' { 2 | interface License { 3 | name: string; 4 | url: string; 5 | osiApproved: boolean; 6 | } 7 | 8 | const list: Record; 9 | 10 | export default list; 11 | } 12 | -------------------------------------------------------------------------------- /scenarios/dual-exports-cjs-mjs/main.cjs: -------------------------------------------------------------------------------- 1 | module.exports = 'dual-exports-cjs-mjs (main)'; 2 | -------------------------------------------------------------------------------- /scenarios/dual-exports-cjs-mjs/module.mjs: -------------------------------------------------------------------------------- 1 | export default 'dual-exports-cjs-mjs (module)'; 2 | -------------------------------------------------------------------------------- /scenarios/dual-exports-cjs-mjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dual-exports-cjs-mjs", 3 | "exports": { 4 | ".": { 5 | "import": "./module.mjs", 6 | "require": "./main.cjs" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scenarios/dual-exports-conditions/browser-default.js: -------------------------------------------------------------------------------- 1 | module.exports = 'dual-exports-conditions (browser-default)'; 2 | -------------------------------------------------------------------------------- /scenarios/dual-exports-conditions/browser.js: -------------------------------------------------------------------------------- 1 | // esm 2 | export default 'dual-exports-conditions (browser)'; 3 | -------------------------------------------------------------------------------- /scenarios/dual-exports-conditions/default.js: -------------------------------------------------------------------------------- 1 | module.exports = 'dual-exports-conditions (default)'; 2 | -------------------------------------------------------------------------------- /scenarios/dual-exports-conditions/node-default.cjs: -------------------------------------------------------------------------------- 1 | module.exports = 'dual-exports-conditions (node-default)'; 2 | -------------------------------------------------------------------------------- /scenarios/dual-exports-conditions/node.cjs: -------------------------------------------------------------------------------- 1 | module.exports = 'dual-exports-conditions (node)'; 2 | -------------------------------------------------------------------------------- /scenarios/dual-exports-conditions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dual-exports-conditions", 3 | "exports": { 4 | ".": { 5 | "browser": { 6 | "import": "./browser.js", 7 | "default": "./browser-default.js" 8 | }, 9 | "node": { 10 | "require": "./node.cjs", 11 | "default": "./node-default.cjs" 12 | }, 13 | "default": "./default.js" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /scenarios/dual-exports-js/main.js: -------------------------------------------------------------------------------- 1 | module.exports = 'dual-exports-js (main)'; 2 | -------------------------------------------------------------------------------- /scenarios/dual-exports-js/module.js: -------------------------------------------------------------------------------- 1 | export default 'dual-exports-js (module)'; 2 | -------------------------------------------------------------------------------- /scenarios/dual-exports-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dual-exports-js", 3 | "exports": { 4 | ".": { 5 | "import": "./module.js", 6 | "require": "./main.js" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scenarios/dual-module-cjs-mjs/main.cjs: -------------------------------------------------------------------------------- 1 | module.exports = 'dual-module-cjs-mjs (main)'; 2 | -------------------------------------------------------------------------------- /scenarios/dual-module-cjs-mjs/module.mjs: -------------------------------------------------------------------------------- 1 | export default 'dual-module-cjs-mjs (module)'; 2 | -------------------------------------------------------------------------------- /scenarios/dual-module-cjs-mjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dual-module-cjs-mjs", 3 | "main": "main.cjs", 4 | "module": "module.mjs" 5 | } 6 | -------------------------------------------------------------------------------- /scenarios/dual-module-js/main.js: -------------------------------------------------------------------------------- 1 | module.exports = 'dual-module-js (main)'; 2 | -------------------------------------------------------------------------------- /scenarios/dual-module-js/module.js: -------------------------------------------------------------------------------- 1 | export default 'dual-module-js (module)'; 2 | -------------------------------------------------------------------------------- /scenarios/dual-module-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dual-module-js", 3 | "main": "main.js", 4 | "module": "module.js" 5 | } 6 | -------------------------------------------------------------------------------- /scenarios/esm-exports-js-module/index.js: -------------------------------------------------------------------------------- 1 | export default 'esm-exports-js-module'; 2 | -------------------------------------------------------------------------------- /scenarios/esm-exports-js-module/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esm-exports-js-module", 3 | "type": "module", 4 | "exports": { 5 | ".": { 6 | "import": "./index.js" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scenarios/esm-exports-js/index.js: -------------------------------------------------------------------------------- 1 | export default 'esm-exports-js'; 2 | -------------------------------------------------------------------------------- /scenarios/esm-exports-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esm-exports-js", 3 | "exports": { 4 | ".": { 5 | "import": "./index.js" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /scenarios/esm-exports-mjs/index.mjs: -------------------------------------------------------------------------------- 1 | export default 'esm-exports-mjs'; 2 | -------------------------------------------------------------------------------- /scenarios/esm-exports-mjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esm-exports-mjs", 3 | "exports": { 4 | ".": { 5 | "import": "./index.mjs" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /scenarios/esm-module-js-module/index.js: -------------------------------------------------------------------------------- 1 | export default 'esm-module-js-module'; 2 | -------------------------------------------------------------------------------- /scenarios/esm-module-js-module/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esm-module-js-module", 3 | "type": "module", 4 | "module": "./index.js" 5 | } 6 | -------------------------------------------------------------------------------- /scenarios/esm-module-js/index.js: -------------------------------------------------------------------------------- 1 | export default 'esm-module-js'; 2 | -------------------------------------------------------------------------------- /scenarios/esm-module-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esm-module-js", 3 | "module": "./index.js" 4 | } 5 | -------------------------------------------------------------------------------- /scenarios/esm-module-mjs/index.mjs: -------------------------------------------------------------------------------- 1 | export default 'esm-module-mjs'; 2 | -------------------------------------------------------------------------------- /scenarios/esm-module-mjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esm-module-mjs", 3 | "module": "./index.mjs" 4 | } 5 | -------------------------------------------------------------------------------- /scenarios/index.js: -------------------------------------------------------------------------------- 1 | // Browser dual CJS/ESM 2 | import dualExportsJs from 'dual-exports-js'; 3 | import dualExportsCjsMjs from 'dual-exports-cjs-mjs'; 4 | import dualModuleJs from 'dual-module-js'; 5 | import dualModuleCjsMjs from 'dual-module-cjs-mjs'; 6 | 7 | // Browser ESM 8 | import esmExportJs from 'esm-exports-js'; 9 | import esmExportJsModule from 'esm-exports-js-module'; 10 | import esmExportMjs from 'esm-exports-mjs'; 11 | import esmModuleJs from 'esm-module-js'; 12 | import esmModuleJsModule from 'esm-module-js-module'; 13 | import esmModuleMjs from 'esm-module-mjs'; 14 | 15 | // Browser/Node conditions 16 | import dualExportsConditions from 'dual-exports-conditions'; 17 | 18 | // These are weird but allowed 19 | import validCjsViaImport from 'valid-cjs-via-import'; 20 | 21 | // These should fail 22 | // import 'invalid-js-module-via-require'; 23 | // import 'invalid-mjs-via-require'; 24 | 25 | console.log( 26 | dualExportsJs, 27 | dualExportsCjsMjs, 28 | dualExportsConditions, 29 | dualModuleJs, 30 | dualModuleCjsMjs, 31 | esmExportJs, 32 | esmExportJsModule, 33 | esmExportMjs, 34 | esmModuleJs, 35 | esmModuleJsModule, 36 | esmModuleMjs, 37 | validCjsViaImport, 38 | ); 39 | -------------------------------------------------------------------------------- /scenarios/invalid-js-module-via-require/index.js: -------------------------------------------------------------------------------- 1 | export default 'invalid-js-module-via-require'; 2 | -------------------------------------------------------------------------------- /scenarios/invalid-js-module-via-require/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "invalid-js-module-via-require", 3 | "type": "module", 4 | "exports": { 5 | ".": { 6 | "require": "./index.js" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scenarios/invalid-mjs-via-require/index.mjs: -------------------------------------------------------------------------------- 1 | export default 'invalid-mjs-via-require'; 2 | -------------------------------------------------------------------------------- /scenarios/invalid-mjs-via-require/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "invalid-mjs-via-require", 3 | "exports": { 4 | ".": { 5 | "require": "./index.mjs" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /scenarios/jest/__tests__/cjs.test.js: -------------------------------------------------------------------------------- 1 | import dualExportsJs from 'dual-exports-js'; 2 | import dualExportsCjsMjs from 'dual-exports-cjs-mjs'; 3 | import dualExportsConditions from 'dual-exports-conditions'; 4 | import dualModuleJs from 'dual-module-js'; 5 | import dualModuleCjsMjs from 'dual-module-cjs-mjs'; 6 | 7 | describe('Resolves', () => { 8 | it('imports cjs/js correctly', async () => { 9 | expect(dualExportsJs).toBe('dual-exports-js (main)'); 10 | expect(dualExportsCjsMjs).toBe('dual-exports-cjs-mjs (main)'); 11 | expect(dualModuleJs).toBe('dual-module-js (main)'); 12 | expect(dualModuleCjsMjs).toBe('dual-module-cjs-mjs (main)'); 13 | 14 | expect(await import('dual-exports-js').then((mod) => mod.default)).toBe( 15 | 'dual-exports-js (main)', 16 | ); 17 | expect(await import('dual-exports-cjs-mjs').then((mod) => mod.default)).toBe( 18 | 'dual-exports-cjs-mjs (main)', 19 | ); 20 | expect(await import('dual-module-js').then((mod) => mod.default)).toBe('dual-module-js (main)'); 21 | expect(await import('dual-module-cjs-mjs').then((mod) => mod.default)).toBe( 22 | 'dual-module-cjs-mjs (main)', 23 | ); 24 | }); 25 | 26 | it('handles conditions', async () => { 27 | expect(dualExportsConditions).toBe('dual-exports-conditions (browser-default)'); 28 | 29 | expect(await import('dual-exports-conditions').then((mod) => mod.default)).toBe( 30 | 'dual-exports-conditions (browser-default)', 31 | ); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /scenarios/jest/__tests__/esm.test.mjs: -------------------------------------------------------------------------------- 1 | describe('Resolves', () => { 2 | // These all fail since Jest doesnt resolve the `import` condition by default for jsdom 3 | it('imports esm', async () => { 4 | // expect(await import('esm-exports-js').then((mod) => mod.default)).toBe('esm-exports-js'); 5 | // expect(await import('esm-exports-js-module').then((mod) => mod.default)).toBe( 6 | // 'esm-exports-js-module', 7 | // ); 8 | // expect(await import('esm-exports-mjs').then((mod) => mod.default)).toBe('esm-exports-mjs'); 9 | // expect(await import('esm-module-js').then((mod) => mod.default)).toBe('esm-module-js'); 10 | // expect(await import('esm-module-js-module').then((mod) => mod.default)).toBe( 11 | // 'esm-module-js-module', 12 | // ); 13 | // expect(await import('esm-module-mjs').then((mod) => mod.default)).toBe('esm-module-mjs'); 14 | }); 15 | 16 | it('handles conditions', async () => { 17 | expect(await import('dual-exports-conditions').then((mod) => mod.default)).toBe( 18 | 'dual-exports-conditions (browser-default)', 19 | ); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /scenarios/jest/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | 'moon', 5 | { 6 | decorators: true, 7 | react: 'automatic', 8 | }, 9 | ], 10 | ], 11 | }; 12 | -------------------------------------------------------------------------------- /scenarios/jest/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'jest-preset-moon', 3 | testEnvironment: 'jsdom', 4 | testEnvironmentOptions: { 5 | customExportConditions: ['browser'], 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /scenarios/jest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "jest ." 5 | }, 6 | "devDependencies": { 7 | "babel-preset-moon": "^3.0.2", 8 | "jest": "^29.7.0", 9 | "jest-environment-jsdom": "^29.7.0", 10 | "jest-preset-moon": "^3.0.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /scenarios/parcel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "parcel build ../index.js --dist-dir build" 5 | }, 6 | "devDependencies": { 7 | "parcel": "^2.12.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scenarios/snowpack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Snowpack App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /scenarios/snowpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "snowpack build" 5 | }, 6 | "devDependencies": { 7 | "snowpack": "^3.8.8" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scenarios/valid-cjs-via-import/index.cjs: -------------------------------------------------------------------------------- 1 | module.exports = 'valid-cjs-via-import'; 2 | -------------------------------------------------------------------------------- /scenarios/valid-cjs-via-import/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "valid-cjs-via-import", 3 | "exports": { 4 | ".": { 5 | "import": "./index.cjs" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /scenarios/vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /scenarios/vite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "vite build --outDir build" 5 | }, 6 | "devDependencies": { 7 | "vite": "^5.2.11" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scenarios/webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "webpack build ../index.js --mode development --output-path ./build --no-devtool" 5 | }, 6 | "devDependencies": { 7 | "webpack": "^5.91.0", 8 | "webpack-cli": "^5.1.4" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /scripts/buildPackages.mjs: -------------------------------------------------------------------------------- 1 | import { execa } from 'execa'; 2 | import path from 'path'; 3 | 4 | async function build(cwd) { 5 | await execa( 6 | 'npx', 7 | ['--yes', '--package', 'packemon', '--package', 'typescript', '--quiet', 'packemon', 'build'], 8 | { 9 | cwd: path.join(process.cwd(), cwd), 10 | preferLocal: true, 11 | stdio: 'inherit', 12 | }, 13 | ); 14 | } 15 | 16 | await build('packages/babel-plugin-cjs-esm-interop'); 17 | await build('packages/babel-plugin-conditional-invariant'); 18 | await build('packages/babel-plugin-env-constants'); 19 | await build('packages/packemon'); 20 | 21 | // We need to link the new binaries to node_modules/.bin 22 | await execa('yarn', ['install']); 23 | -------------------------------------------------------------------------------- /scripts/deleteSwcTypes.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | 3 | // This file causes issues with Jest tests! 4 | // It's ok to delete through since there is a d.ts file. 5 | const path = 'node_modules/@swc/types/index.ts'; 6 | 7 | if (fs.existsSync(path)) { 8 | fs.unlinkSync(path); 9 | } 10 | -------------------------------------------------------------------------------- /scripts/testSwc.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import { transformSync, transformFileSync } from '@swc/core'; 3 | 4 | const filename = './packages/packemon/src/babel/config.ts'; 5 | 6 | try { 7 | fs.mkdirSync(new URL('./swc', import.meta.url)); 8 | } catch {} 9 | 10 | const input = transformFileSync(filename, { 11 | jsc: { 12 | parser: { syntax: 'typescript', tsx: true, decorators: true }, 13 | transform: { 14 | optimizer: { 15 | globals: { 16 | vars: { 17 | __DEV__: "process.env.NODE_ENV !== 'production'", 18 | __PROD__: "process.env.NODE_ENV === 'production'", 19 | __TEST__: "process.env.NODE_ENV === 'test'", 20 | }, 21 | }, 22 | }, 23 | legacyDecorator: true, 24 | decoratorMetadata: false, 25 | react: { 26 | development: false, 27 | runtime: 'automatic', 28 | throwIfNamespace: true, 29 | }, 30 | }, 31 | externalHelpers: false, 32 | loose: false, 33 | keepClassNames: true, 34 | target: 'es2022', 35 | }, 36 | caller: { name: 'packemon' }, 37 | configFile: false, 38 | swcrc: false, 39 | exclude: [ 40 | 'node_modules', 41 | 'tests', 42 | '__fixtures__', 43 | '__mocks__', 44 | '__tests__', 45 | '\\.(config|test|spec)\\.[a-z]+$', 46 | '\\.(test|spec)\\.[a-z]+$', 47 | ], 48 | sourceMaps: true, 49 | filename, 50 | }); 51 | 52 | fs.writeFileSync(new URL('./swc/input-pass.js', import.meta.url), input.code, 'utf8'); 53 | 54 | function getOutputConfig(type) { 55 | return { 56 | env: { 57 | loose: false, 58 | mode: undefined, 59 | bugfixes: true, 60 | shippedProposals: true, 61 | targets: { 62 | node: '14.15.0', 63 | }, 64 | }, 65 | module: { 66 | type, 67 | ignoreDynamic: true, 68 | }, 69 | jsc: { 70 | parser: { 71 | syntax: 'ecmascript', 72 | }, 73 | transform: { 74 | optimizer: undefined, 75 | }, 76 | target: 'es5', 77 | keepClassNames: true, 78 | preserveAllComments: false, 79 | }, 80 | caller: { name: 'packemon' }, 81 | configFile: false, 82 | swcrc: false, 83 | exclude: [], 84 | sourceMaps: true, 85 | filename, 86 | }; 87 | } 88 | 89 | fs.writeFileSync( 90 | new URL('./swc/output-cjs.js', import.meta.url), 91 | transformSync(input.code, getOutputConfig('commonjs')).code, 92 | 'utf8', 93 | ); 94 | 95 | fs.writeFileSync( 96 | new URL('./swc/output-esm.js', import.meta.url), 97 | transformSync(input.code, getOutputConfig('es6')).code, 98 | 'utf8', 99 | ); 100 | 101 | fs.writeFileSync( 102 | new URL('./swc/output-umd.js', import.meta.url), 103 | transformSync(input.code, getOutputConfig('umd')).code, 104 | 'utf8', 105 | ); 106 | -------------------------------------------------------------------------------- /tests/__fixtures__/config-files-monorepo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "workspaces": [ 4 | "packages/*" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /tests/__fixtures__/config-files-monorepo/packages/bar/.packemon.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | babelInput(config) { 3 | config.plugins.push('babel-plugin-bar'); 4 | }, 5 | babelOutput(config, build) { 6 | config.plugins.push(['bar-plugin', build]); 7 | }, 8 | rollupInput(config) { 9 | config.plugins.push({ name: 'rollup-plugin-bar' }); 10 | }, 11 | rollupOutput(config, build) { 12 | config.plugins.push({ name: 'bar-plugin', ...build }); 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /tests/__fixtures__/config-files-monorepo/packages/bar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "monorepo-bar", 3 | "packemon": { 4 | "platform": "node" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/__fixtures__/config-files-monorepo/packages/baz/.packemon.ts: -------------------------------------------------------------------------------- 1 | import { ConfigFile } from 'packemon'; 2 | 3 | const config: ConfigFile = { 4 | babelInput(config) { 5 | config.plugins.unshift('babel-plugin-baz'); 6 | }, 7 | babelOutput(config, build) { 8 | config.plugins.unshift(['baz-plugin', build]); 9 | }, 10 | rollupInput(config) { 11 | config.plugins.unshift({ name: 'rollup-plugin-baz' }); 12 | }, 13 | rollupOutput(config, build) { 14 | config.plugins.unshift({ name: 'baz-plugin', ...build }); 15 | }, 16 | }; 17 | 18 | export default config; 19 | -------------------------------------------------------------------------------- /tests/__fixtures__/config-files-monorepo/packages/baz/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "monorepo-baz", 3 | "packemon": { 4 | "platform": "browser" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/__fixtures__/config-files-monorepo/packages/foo/.packemon.ts: -------------------------------------------------------------------------------- 1 | import { ConfigFile } from 'packemon'; 2 | 3 | const config: ConfigFile = { 4 | babelInput(config) { 5 | config.plugins.push('babel-plugin-foo'); 6 | }, 7 | babelOutput(config, build) { 8 | config.plugins.push(['foo-plugin', build]); 9 | }, 10 | rollupInput(config) { 11 | config.plugins.push({ name: 'rollup-plugin-foo' }); 12 | }, 13 | rollupOutput(config, build) { 14 | config.plugins.push({ name: 'foo-plugin', ...build }); 15 | }, 16 | }; 17 | 18 | export default config; 19 | -------------------------------------------------------------------------------- /tests/__fixtures__/config-files-monorepo/packages/foo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "monorepo-foo", 3 | "packemon": { 4 | "platform": "node" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/__fixtures__/config-files-monorepo/packemon.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | babelInput(config) { 3 | config.plugins.push('babel-plugin-root'); 4 | }, 5 | babelOutput(config) { 6 | config.plugins.push('root-plugin'); 7 | }, 8 | rollupInput(config) { 9 | config.plugins.push({ name: 'rollup-plugin-root' }); 10 | }, 11 | rollupOutput(config) { 12 | config.plugins.push({ name: 'root-plugin' }); 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /tests/__fixtures__/config-files-polyrepo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "config-files-polyrepo", 3 | "packemon": { 4 | "platform": "browser" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/__fixtures__/config-files-polyrepo/packemon.config.ts: -------------------------------------------------------------------------------- 1 | import { ConfigFile } from 'packemon'; 2 | 3 | const config: ConfigFile = { 4 | babelInput(config) { 5 | config.plugins.push('poly-plugin-input'); 6 | }, 7 | babelOutput(config, build) { 8 | config.plugins.unshift(['poly-plugin-output', build]); 9 | }, 10 | rollupInput(config) { 11 | config.plugins.push({ name: 'poly-plugin-input' }); 12 | }, 13 | rollupOutput(config, build) { 14 | config.plugins.unshift({ name: 'poly-plugin-output', ...build }); 15 | }, 16 | }; 17 | 18 | export default config; 19 | -------------------------------------------------------------------------------- /tests/__fixtures__/example-solid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "examples", 3 | "packemon": {}, 4 | "files": [ 5 | "cjs/**/*.{cjs,map}", 6 | "esm/**/*.{js,map}", 7 | "lib/**/*.{js,map}", 8 | "mjs/**/*.{mjs,map}", 9 | "src/**/*.{ts,tsx,json}", 10 | "umd/**/*.{js,map}" 11 | ], 12 | "peerDependencies": { 13 | "solid-js": "^1.5.1" 14 | }, 15 | "dependencies": { 16 | "optimal": "*" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/__fixtures__/example-solid/solid.tsx: -------------------------------------------------------------------------------- 1 | export function Button() { 2 | return