diff --git a/packages/packemon/package.json b/packages/packemon/package.json index 35f9d9dfc..775a38315 100644 --- a/packages/packemon/package.json +++ b/packages/packemon/package.json @@ -28,38 +28,23 @@ "./package.json": "./package.json", "./babel": { "node": { - "import": { - "types": "./cjs/babel.d.ts", - "default": "./cjs/babel-wrapper.mjs" - }, - "require": { - "types": "./cjs/babel.d.ts", - "default": "./cjs/babel.cjs" - } + "types": "./cjs/babel.d.ts", + "import": "./cjs/babel-wrapper.mjs", + "require": "./cjs/babel.cjs" } }, "./bin": { "node": { - "import": { - "types": "./cjs/bin.d.ts", - "default": "./cjs/bin-wrapper.mjs" - }, - "require": { - "types": "./cjs/bin.d.ts", - "default": "./cjs/bin.cjs" - } + "types": "./cjs/bin.d.ts", + "import": "./cjs/bin-wrapper.mjs", + "require": "./cjs/bin.cjs" } }, ".": { "node": { - "import": { - "types": "./cjs/index.d.ts", - "default": "./cjs/index-wrapper.mjs" - }, - "require": { - "types": "./cjs/index.d.ts", - "default": "./cjs/index.cjs" - } + "types": "./cjs/index.d.ts", + "import": "./cjs/index-wrapper.mjs", + "require": "./cjs/index.cjs" } } }, diff --git a/packages/packemon/src/Artifact.ts b/packages/packemon/src/Artifact.ts index a54d64693..a5b665f28 100644 --- a/packages/packemon/src/Artifact.ts +++ b/packages/packemon/src/Artifact.ts @@ -2,7 +2,7 @@ import execa from 'execa'; import fs from 'fs-extra'; import { rollup } from 'rollup'; import { applyStyle } from '@boost/cli'; -import { Path, toArray, VirtualPath } from '@boost/common'; +import { isObject, Path, toArray, VirtualPath } from '@boost/common'; import { createDebugger, Debugger } from '@boost/debug'; import { removeSourcePath } from './helpers/removeSourcePath'; import type { Package } from './Package'; @@ -73,9 +73,11 @@ export class Artifact { /** * Build code and types in parallel. */ - async build(options: BuildOptions, packemonConfig: ConfigFile): Promise { - const features = this.package.getFeatureFlags(); - + async build( + options: BuildOptions, + features: FeatureFlags, + packemonConfig: ConfigFile, + ): Promise { await Promise.all([this.buildCode(features, packemonConfig), this.buildTypes(features)]); } @@ -263,18 +265,18 @@ export class Artifact { return `${this.platform}:${this.support}:${this.builds.map((build) => build.format).join(',')}`; } - getPackageExports(): PackageExports { + getPackageExports(features: FeatureFlags): PackageExports { const exportMap: PackageExports = {}; if (this.api === 'private' || this.bundle) { Object.keys(this.inputs).forEach((outputName) => { - this.mapPackageExportsFromBuilds(outputName, exportMap); + this.mapPackageExportsFromBuilds(outputName, exportMap, features); }); } else { // Use subpath export patterns when not bundling // https://nodejs.org/api/packages.html#subpath-patterns - this.mapPackageExportsFromBuilds('*', exportMap); - this.mapPackageExportsFromBuilds(this.getIndexInput(), exportMap, true); + this.mapPackageExportsFromBuilds('*', exportMap, features); + this.mapPackageExportsFromBuilds(this.getIndexInput(), exportMap, features, true); } return exportMap; @@ -296,6 +298,7 @@ export class Artifact { protected mapPackageExportsFromBuilds( outputName: string, exportMap: PackageExports, + features: FeatureFlags, index: boolean = false, ) { const defaultEntry = this.findEntryPoint(['lib'], outputName); @@ -327,42 +330,49 @@ export class Artifact { const mjsEntry = this.findEntryPoint(['mjs'], outputName); const cjsEntry = this.findEntryPoint(['cjs'], outputName); - if (mjsEntry || cjsEntry) { + if (mjsEntry && cjsEntry) { + paths = { + import: { + types: mjsEntry.declPath, + default: mjsEntry.entryPath, + }, + require: { + types: cjsEntry.declPath, + default: cjsEntry.entryPath, + }, + }; + } else if (mjsEntry) { paths = { - import: mjsEntry - ? { - types: mjsEntry.declPath, - default: mjsEntry.entryPath, - } - : undefined, - require: cjsEntry - ? { - types: cjsEntry.declPath, - default: cjsEntry.entryPath, - } - : undefined, + types: mjsEntry.declPath, + import: mjsEntry.entryPath, + }; + } else if (cjsEntry) { + paths = { + types: cjsEntry.declPath, + require: cjsEntry.entryPath, }; // Automatically apply the mjs wrapper for cjs - if (!paths.import && outputName !== '*' && cjsEntry) { - paths.import = { - types: cjsEntry.declPath, - default: cjsEntry.entryPath.replace('.cjs', '-wrapper.mjs'), - }; + if (!paths.import && outputName !== '*') { + paths.import = cjsEntry.entryPath.replace('.cjs', '-wrapper.mjs'); + } + } + + if (defaultEntry) { + if (!paths.types && !isObject(paths.import) && !isObject(paths.require)) { + paths.types = defaultEntry.declPath; } - if (!paths.require && defaultEntry) { + if (!paths.require) { paths.default = defaultEntry.entryPath; } - } else { - paths.types = defaultEntry?.declPath; - paths.default = defaultEntry?.entryPath; } break; } case 'native': + paths.types = defaultEntry?.declPath; paths.default = defaultEntry?.entryPath; break; @@ -370,15 +380,24 @@ export class Artifact { break; } - const pathsMap = { + const pathsMap: Record = { [this.platform === 'native' ? 'react-native' : this.platform]: paths, }; + // Point to the source files when using solid + if (features.solid) { + if (outputName === '*') { + pathsMap.solid = `./src/*.${features.typescript ? 'tsx' : 'js'}`; + } else { + const input = this.inputs[outputName]; + + pathsMap.solid = input.startsWith('./') ? input : `./${input}`; + } + } + // Provide fallbacks if condition above is not if (defaultEntry) { - Object.assign(pathsMap, { - default: defaultEntry.entryPath, - }); + pathsMap.default = defaultEntry.entryPath; } // eslint-disable-next-line no-param-reassign diff --git a/packages/packemon/src/Package.ts b/packages/packemon/src/Package.ts index 7dd3b17b3..7e9334337 100644 --- a/packages/packemon/src/Package.ts +++ b/packages/packemon/src/Package.ts @@ -71,6 +71,8 @@ export class Package { this.debug('Building artifacts'); // Build artifacts in parallel + const features = this.getFeatureFlags(); + await Promise.all( this.artifacts.map(async (artifact) => { const start = Date.now(); @@ -78,7 +80,7 @@ export class Package { try { artifact.state = 'building'; - await artifact.build(options, packemonConfig); + await artifact.build(options, features, packemonConfig); artifact.state = 'passed'; } catch (error: unknown) { @@ -101,7 +103,7 @@ export class Package { // Add package `exports` based on artifacts if (options.addExports) { - this.addExports(); + this.addExports(features); } // Add package `files` whitelist @@ -473,7 +475,7 @@ export class Package { } } - protected addExports() { + protected addExports(features: FeatureFlags) { this.debug('Adding `exports` to `package.json`'); let exportMap: PackageExports = { @@ -481,7 +483,7 @@ export class Package { }; this.artifacts.forEach((artifact) => { - Object.entries(artifact.getPackageExports()).forEach(([path, conditions]) => { + Object.entries(artifact.getPackageExports(features)).forEach(([path, conditions]) => { if (conditions) { exportMap[path] = mergeExports(exportMap[path] ?? {}, conditions); } diff --git a/packages/packemon/src/babel/config.ts b/packages/packemon/src/babel/config.ts index a0d4bccfc..a715f8f89 100644 --- a/packages/packemon/src/babel/config.ts +++ b/packages/packemon/src/babel/config.ts @@ -119,7 +119,7 @@ export function getBabelInputConfig( const presets: PluginItem[] = []; const tsOptions = { allowDeclareFields: true, - onlyRemoveTypeImports: true, + onlyRemoveTypeImports: false, optimizeConstEnums: true, }; diff --git a/packages/packemon/tests/Artifact.test.ts b/packages/packemon/tests/Artifact.test.ts index 2deca5212..715648df0 100644 --- a/packages/packemon/tests/Artifact.test.ts +++ b/packages/packemon/tests/Artifact.test.ts @@ -54,7 +54,7 @@ describe('Artifact', () => { const codeSpy = jest.spyOn(artifact, 'buildCode').mockImplementation(); const typesSpy = jest.spyOn(artifact, 'buildTypes').mockImplementation(); - await artifact.build({}, {}); + await artifact.build({}, {}, {}); expect(codeSpy).toHaveBeenCalled(); expect(typesSpy).toHaveBeenCalled(); @@ -336,7 +336,7 @@ describe('Artifact', () => { it('adds exports based on input file and output name', () => { artifact.builds.push({ format: 'lib' }); - expect(artifact.getPackageExports()).toEqual({ + expect(artifact.getPackageExports({})).toEqual({ '.': { node: { types: undefined, @@ -351,7 +351,7 @@ describe('Artifact', () => { artifact.sharedLib = true; artifact.builds.push({ format: 'lib' }); - expect(artifact.getPackageExports()).toEqual({ + expect(artifact.getPackageExports({})).toEqual({ '.': { node: { types: undefined, @@ -366,7 +366,7 @@ describe('Artifact', () => { artifact.inputs = { sub: './src/sub.ts' }; artifact.builds.push({ format: 'lib' }); - expect(artifact.getPackageExports()).toEqual({ + expect(artifact.getPackageExports({})).toEqual({ './sub': { node: { types: undefined, @@ -380,7 +380,7 @@ describe('Artifact', () => { it('supports conditional exports when there are multiple builds', () => { artifact.builds.push({ format: 'lib' }, { format: 'mjs' }, { format: 'cjs' }); - expect(artifact.getPackageExports()).toEqual({ + expect(artifact.getPackageExports({})).toEqual({ '.': { node: { import: { @@ -404,7 +404,7 @@ describe('Artifact', () => { { declaration: true, format: 'cjs' }, ); - expect(artifact.getPackageExports()).toEqual({ + expect(artifact.getPackageExports({})).toEqual({ '.': { node: { import: { @@ -425,7 +425,7 @@ describe('Artifact', () => { artifact.inputs = { sub: './src/sub.ts' }; artifact.builds.push({ format: 'mjs' }, { format: 'cjs' }); - expect(artifact.getPackageExports()).toEqual({ + expect(artifact.getPackageExports({})).toEqual({ './sub': { node: { import: { @@ -445,7 +445,7 @@ describe('Artifact', () => { artifact.platform = 'browser'; artifact.builds.push({ format: 'lib' }); - expect(artifact.getPackageExports()).toEqual({ + expect(artifact.getPackageExports({})).toEqual({ '.': { browser: { types: undefined, @@ -460,7 +460,7 @@ describe('Artifact', () => { artifact.platform = 'native'; artifact.builds.push({ format: 'lib' }); - expect(artifact.getPackageExports()).toEqual({ + expect(artifact.getPackageExports({})).toEqual({ '.': { 'react-native': { default: './lib/index.js', @@ -473,7 +473,7 @@ describe('Artifact', () => { it('supports lib', () => { artifact.builds.push({ format: 'lib' }); - expect(artifact.getPackageExports()).toEqual({ + expect(artifact.getPackageExports({})).toEqual({ '.': { node: { types: undefined, @@ -487,7 +487,7 @@ describe('Artifact', () => { it('supports lib with types', () => { artifact.builds.push({ declaration: true, format: 'lib' }); - expect(artifact.getPackageExports()).toEqual({ + expect(artifact.getPackageExports({})).toEqual({ '.': { node: { types: './lib/index.d.ts', @@ -501,17 +501,11 @@ describe('Artifact', () => { it('supports cjs', () => { artifact.builds.push({ format: 'cjs' }); - expect(artifact.getPackageExports()).toEqual({ + expect(artifact.getPackageExports({})).toEqual({ '.': { node: { - import: { - types: undefined, - default: './cjs/index-wrapper.mjs', - }, - require: { - types: undefined, - default: './cjs/index.cjs', - }, + import: './cjs/index-wrapper.mjs', + require: './cjs/index.cjs', }, }, }); @@ -520,17 +514,12 @@ describe('Artifact', () => { it('supports cjs with types', () => { artifact.builds.push({ declaration: true, format: 'cjs' }); - expect(artifact.getPackageExports()).toEqual({ + expect(artifact.getPackageExports({})).toEqual({ '.': { node: { - import: { - types: './cjs/index.d.ts', - default: './cjs/index-wrapper.mjs', - }, - require: { - types: './cjs/index.d.ts', - default: './cjs/index.cjs', - }, + types: './cjs/index.d.ts', + import: './cjs/index-wrapper.mjs', + require: './cjs/index.cjs', }, }, }); @@ -540,17 +529,12 @@ describe('Artifact', () => { artifact.builds.push({ declaration: true, format: 'cjs' }); artifact.inputs = { index: 'src/index.cts' }; - expect(artifact.getPackageExports()).toEqual({ + expect(artifact.getPackageExports({})).toEqual({ '.': { node: { - import: { - types: './cjs/index.d.cts', - default: './cjs/index-wrapper.mjs', - }, - require: { - types: './cjs/index.d.cts', - default: './cjs/index.cjs', - }, + types: './cjs/index.d.cts', + import: './cjs/index-wrapper.mjs', + require: './cjs/index.cjs', }, }, }); @@ -559,14 +543,10 @@ describe('Artifact', () => { it('supports mjs', () => { artifact.builds.push({ format: 'mjs' }); - expect(artifact.getPackageExports()).toEqual({ + expect(artifact.getPackageExports({})).toEqual({ '.': { node: { - import: { - types: undefined, - default: './mjs/index.mjs', - }, - require: undefined, + import: './mjs/index.mjs', }, }, }); @@ -575,14 +555,11 @@ describe('Artifact', () => { it('supports mjs with types', () => { artifact.builds.push({ declaration: true, format: 'mjs' }); - expect(artifact.getPackageExports()).toEqual({ + expect(artifact.getPackageExports({})).toEqual({ '.': { node: { - import: { - types: './mjs/index.d.ts', - default: './mjs/index.mjs', - }, - require: undefined, + types: './mjs/index.d.ts', + import: './mjs/index.mjs', }, }, }); @@ -592,18 +569,90 @@ describe('Artifact', () => { artifact.builds.push({ declaration: true, format: 'mjs' }); artifact.inputs = { index: 'src/index.mts' }; - expect(artifact.getPackageExports()).toEqual({ + expect(artifact.getPackageExports({})).toEqual({ '.': { node: { - import: { - types: './mjs/index.d.mts', - default: './mjs/index.mjs', - }, - require: undefined, + types: './mjs/index.d.mts', + import: './mjs/index.mjs', }, }, }); }); + + describe('solid', () => { + it('adds entry point to source code', () => { + artifact.builds.push({ format: 'lib' }); + + expect(artifact.getPackageExports({ solid: true })).toEqual({ + '.': { + node: { + types: undefined, + default: './lib/index.js', + }, + solid: './src/index.ts', + default: './lib/index.js', + }, + }); + }); + + it('supports non-bundle', () => { + artifact.api = 'public'; + artifact.bundle = false; + artifact.builds.push({ format: 'lib' }); + + expect(artifact.getPackageExports({ solid: true })).toEqual({ + '.': { + node: { + types: undefined, + default: './lib/index.js', + }, + solid: './src/index.ts', + default: './lib/index.js', + }, + './*': { + node: { + types: undefined, + default: './lib/*.js', + }, + solid: './src/*.js', + default: './lib/*.js', + }, + }); + }); + + it('supports shared lib', () => { + artifact.sharedLib = true; + artifact.builds.push({ format: 'lib' }); + + expect(artifact.getPackageExports({ solid: true })).toEqual({ + '.': { + node: { + types: undefined, + default: './lib/node/index.js', + }, + solid: './src/index.ts', + default: './lib/node/index.js', + }, + }); + }); + + it('supports types', () => { + artifact.platform = 'browser'; + artifact.builds.push({ declaration: true, format: 'esm' }); + + expect(artifact.getPackageExports({ solid: true })).toEqual({ + '.': { + browser: { + default: undefined, + import: './esm/index.js', + module: './esm/index.js', + types: './esm/index.d.ts', + }, + solid: './src/index.ts', + }, + }); + }); + }); }); describe('logWithSource()', () => { diff --git a/packages/packemon/tests/Package.test.ts b/packages/packemon/tests/Package.test.ts index 02f80f3b4..67a2b31bd 100644 --- a/packages/packemon/tests/Package.test.ts +++ b/packages/packemon/tests/Package.test.ts @@ -75,9 +75,9 @@ describe('Packemon', () => { await pkg.build({ concurrency: 1 }, config); - expect(aSpy).toHaveBeenCalledWith({ concurrency: 1 }, expect.any(Object)); - expect(bSpy).toHaveBeenCalledWith({ concurrency: 1 }, expect.any(Object)); - expect(cSpy).toHaveBeenCalledWith({ concurrency: 1 }, expect.any(Object)); + expect(aSpy).toHaveBeenCalledWith({ concurrency: 1 }, expect.any(Object), expect.any(Object)); + expect(bSpy).toHaveBeenCalledWith({ concurrency: 1 }, expect.any(Object), expect.any(Object)); + expect(cSpy).toHaveBeenCalledWith({ concurrency: 1 }, expect.any(Object), expect.any(Object)); }); it('sets passed state and result time', async () => { diff --git a/packages/packemon/tests/babel/__snapshots__/config.test.ts.snap b/packages/packemon/tests/babel/__snapshots__/config.test.ts.snap index 55d3d8c4e..0f03c3a5a 100644 --- a/packages/packemon/tests/babel/__snapshots__/config.test.ts.snap +++ b/packages/packemon/tests/babel/__snapshots__/config.test.ts.snap @@ -116,7 +116,7 @@ exports[`getBabelInputConfig() includes typescript decorators if \`typescript\` "@babel/plugin-transform-typescript", { "allowDeclareFields": true, - "onlyRemoveTypeImports": true, + "onlyRemoveTypeImports": false, "optimizeConstEnums": true, }, ], @@ -150,7 +150,7 @@ exports[`getBabelInputConfig() includes typescript decorators if \`typescript\` "@babel/preset-typescript", { "allowDeclareFields": true, - "onlyRemoveTypeImports": true, + "onlyRemoveTypeImports": false, "optimizeConstEnums": true, }, ], @@ -164,7 +164,7 @@ exports[`getBabelInputConfig() includes typescript preset if \`typescript\` feat "@babel/preset-typescript", { "allowDeclareFields": true, - "onlyRemoveTypeImports": true, + "onlyRemoveTypeImports": false, "optimizeConstEnums": true, }, ], @@ -188,7 +188,7 @@ exports[`getBabelInputConfig() supports private properties with decorators if de "@babel/plugin-transform-typescript", { "allowDeclareFields": true, - "onlyRemoveTypeImports": true, + "onlyRemoveTypeImports": false, "optimizeConstEnums": true, }, ], @@ -222,7 +222,7 @@ exports[`getBabelInputConfig() supports private properties with decorators if de "@babel/preset-typescript", { "allowDeclareFields": true, - "onlyRemoveTypeImports": true, + "onlyRemoveTypeImports": false, "optimizeConstEnums": true, }, ],