Comparing version 1.5.0 to 1.6.0
@@ -5,3 +5,3 @@ import * as console from './console.js'; | ||
import './tsconfig.js'; | ||
import { unlink as unlinkImports } from './imports.js'; | ||
import { unlink as unlinkImports } from './unbuilt-imports.js'; | ||
import { unlink as unlinkSelfDep } from './self-dep.js'; | ||
@@ -8,0 +8,0 @@ import pkg from './package.js'; |
@@ -9,3 +9,3 @@ import chalk from 'chalk'; | ||
import dialects from './dialects.js'; | ||
import { link as linkImports, save as saveImports, unlink as unlinkImports, } from './imports.js'; | ||
import { link as linkImports, save as saveImports, unlink as unlinkImports, } from './unbuilt-imports.js'; | ||
import pkg from './package.js'; | ||
@@ -12,0 +12,0 @@ import { link as linkSelfDep, unlink as unlinkSelfDep, } from './self-dep.js'; |
// get the config and package and stuff | ||
import chalk from 'chalk'; | ||
import * as console from './console.js'; | ||
import fail from './fail.js'; | ||
@@ -22,6 +24,16 @@ import pkg from './package.js'; | ||
validBoolean(e, 'selfLink') && | ||
validBoolean(e, 'main') && | ||
validImports(e, pkg); | ||
validBoolean(e, 'main'); | ||
const getConfig = (pkg, sources) => { | ||
const tshy = validConfig(pkg.tshy) ? pkg.tshy : {}; | ||
const ti = tshy; | ||
if (ti.imports) { | ||
console.debug(chalk.cyan.dim('imports') + | ||
' moving from tshy config to top level'); | ||
pkg.imports = { | ||
...pkg.imports, | ||
...ti.imports, | ||
}; | ||
delete ti.imports; | ||
} | ||
validImports(pkg); | ||
if (tshy.exports) | ||
@@ -28,0 +40,0 @@ return tshy; |
@@ -5,3 +5,3 @@ import chalk from 'chalk'; | ||
import * as console from './console.js'; | ||
import getImports from './get-imports.js'; | ||
import getImports from './built-imports.js'; | ||
import pkg from './package.js'; | ||
@@ -8,0 +8,0 @@ const writeDialectPJ = (d, mode) => { |
@@ -8,7 +8,6 @@ import chalk from 'chalk'; | ||
// the commonjs build needs to exclude anything that will be polyfilled | ||
import { addToFile, addToObject } from './add-paths-to-tsconfig.js'; | ||
import config from './config.js'; | ||
import polyfills from './polyfills.js'; | ||
const { dialects = ['esm', 'commonjs'], esmDialects = [], commonjsDialects = [], } = config; | ||
const recommended = addToObject({ | ||
const recommended = { | ||
compilerOptions: { | ||
@@ -30,3 +29,3 @@ declaration: true, | ||
}, | ||
}); | ||
}; | ||
const build = { | ||
@@ -87,3 +86,2 @@ extends: '../tsconfig.json', | ||
console.debug('using existing tsconfig.json'); | ||
addToFile(); | ||
} | ||
@@ -90,0 +88,0 @@ for (const f of readdirSync('.tshy')) { |
import type { ConditionalValue, ExportsSubpaths, Imports } from 'resolve-import'; | ||
export type TshyConfig = { | ||
exports?: Record<string, TshyExport>; | ||
imports?: Record<string, string>; | ||
dialects?: Dialect[]; | ||
@@ -6,0 +5,0 @@ selfLink?: boolean; |
@@ -1,4 +0,4 @@ | ||
import { Package, TshyConfig } from './types.js'; | ||
declare const _default: ({ imports }: TshyConfig, pkg: Package) => boolean; | ||
import { Package } from './types.js'; | ||
declare const _default: (pkg: Package) => boolean; | ||
export default _default; | ||
//# sourceMappingURL=valid-imports.d.ts.map |
import fail from './fail.js'; | ||
// this validates the tshy.imports field. | ||
export default ({ imports }, pkg) => { | ||
import validExternalExport from './valid-external-export.js'; | ||
// validate the package.imports field | ||
export default (pkg) => { | ||
const { imports } = pkg; | ||
if (imports === undefined) | ||
return true; | ||
const { imports: pkgImports = {} } = pkg; | ||
if (typeof imports !== 'object' || Array.isArray(imports)) { | ||
fail('tshy.imports must be an object if specified'); | ||
if (Array.isArray(imports) || typeof imports !== 'object') { | ||
fail('invalid imports object, must be Record<string, Import>, ' + | ||
`got: ${JSON.stringify(imports)}`); | ||
return process.exit(1); | ||
} | ||
for (const [i, v] of Object.entries(imports)) { | ||
if (i in pkgImports) { | ||
fail('tshy.imports keys must not appear in top-level imports, ' + | ||
'found in both: ' + | ||
JSON.stringify(i)); | ||
return process.exit(1); | ||
} | ||
if (!i.startsWith('#') || i === '#' || i.startsWith('#/')) { | ||
fail('invalid tshy.imports module specifier: ' + i); | ||
fail('invalid imports module specifier: ' + i); | ||
return process.exit(1); | ||
} | ||
if (typeof v !== 'string' || !v.startsWith('./src/')) { | ||
fail('tshy.imports values must start with "./src/", ' + | ||
'got: ' + | ||
if (typeof v === 'string') | ||
continue; | ||
if (!validExternalExport(v)) { | ||
fail(`unbuilt package.imports ${i} must not be in ./src, ` + | ||
'and imports in ./src must be string values. got: ' + | ||
JSON.stringify(v)); | ||
@@ -26,0 +24,0 @@ return process.exit(1); |
{ | ||
"name": "tshy", | ||
"version": "1.5.0", | ||
"version": "1.6.0", | ||
"description": "TypeScript HYbridizer - Hybrid (CommonJS/ESM) TypeScript node package builder", | ||
@@ -5,0 +5,0 @@ "author": "Isaac Z. Schlueter <i@izs.me> (https://izs.me)", |
157
README.md
@@ -116,23 +116,56 @@ # tshy - TypeScript HYbridizer | ||
### `tshy.imports` | ||
### Package `#imports` | ||
You can use Node `package.json` `imports` in the `tshy` config, | ||
referencing input files in `./src`. These will be copied into the | ||
`package.json` files built into the `dist/{esm,commonjs}` | ||
folders, so that they work like they would in a normal Node | ||
program. | ||
You can use `"imports"` in your package.json, and it will be | ||
handled in the following ways. | ||
The `tshy.imports` entries: | ||
### Built Imports | ||
- Must have a string key starting with `#`. | ||
- Must have a string value starting with `'./src'`. | ||
Any `"imports"` that resolve to a file built as part of your | ||
program must be a non-conditional string value pointing to the | ||
file in `./src/`. For example: | ||
For example, you can do this: | ||
```json | ||
{ | ||
"imports": { | ||
"#name": "./src/path/to/name.ts", | ||
"#utils/*": "./src/path/to/utils/*.ts" | ||
} | ||
} | ||
``` | ||
In the ESM build, `import * from '#name'` will resolve to | ||
`./dist/esm/path/to/name.js`, and will be built for ESM. In the | ||
CommonJS build, `require('#name')` will resolve to | ||
`./dist/commonjs/path/to/name.js` and will be built for CommonJS. | ||
<details> | ||
<summary>tl;dr how this works and why it can't be conditional</summary> | ||
In the built `dist/{dialect}/package.json` files, the `./src` | ||
will be stripped from the path and their file extension changed | ||
from `ts` to `js` (`cts` to `cjs` and `mts` to `mjs`). | ||
It shouldn't be conditional, because the condition is already | ||
implicit in the build. In the CommonJS build, they should be | ||
required, and in the ESM builds, they should be imported, and | ||
there's only one thing that it can resolve to from any given | ||
build. | ||
</details> | ||
Any `"imports"` that resolve to something _not_ built by tshy, | ||
then tshy will set `scripts.preinstall` to set up symbolic links | ||
to make it work at install time. This just means that you can't | ||
use `scripts.preinstall` for anything else if you have | ||
`"imports"` that aren't managed by tshy. For example: | ||
```json | ||
{ | ||
"tshy": { | ||
"imports": { | ||
"#foo": "./src/lib/foo.ts", | ||
"#utils/*": "./src/app/shared-components/utils/*" | ||
"imports": { | ||
"#dep": "@scope/dep/submodule", | ||
"#conditional": { | ||
"types": "./vendor/blah.d.ts", | ||
"require": "./vendor/blah.cjs", | ||
"import": "./vendor/blah.mjs | ||
} | ||
@@ -143,29 +176,41 @@ } | ||
Then in your program, you can do this: | ||
<details> | ||
<summary>tl;dr explanation</summary> | ||
```ts | ||
// src/index.ts | ||
import { foo } from '#foo' | ||
import { barUtil } from '#utils/bar.js' | ||
``` | ||
The `"imports"` field in package.json allows you to set local | ||
package imports, which have the same kind of conditional import | ||
logic as `"exports"`. This is especially useful when you have a | ||
vendored dependency with `require` and `import` variants, modules | ||
that have to be bundled in different ways for different | ||
environments, or different dependencies for different | ||
environments. | ||
When this is compiled to `./dist/esm/index.js`, it will | ||
automatically map `#foo` to `./dist/esm/lib/foo.js` and | ||
`#utils/bar.js` to | ||
`./dist/esm/app/shared-components/utils/bar.js`. | ||
These package imports are _always_ resolved against the nearest | ||
`package.json` file, and tshy uses generated package.json files | ||
to set the module dialect to `"type":"module"` in `dist/esm` and | ||
`"type":"commonjs"` in `dist/commonjs`, and it swaps the | ||
`src/package.json` file between this during the `tsc` builds. | ||
When using this feature, `tshy` will automatically update your | ||
`./tsconfig.json` file to set the | ||
[`paths`](https://www.typescriptlang.org/tsconfig#paths) | ||
appropriately so that it can find the types. | ||
Furthermore, local package imports may not be relative files | ||
outside the package folder. They may only be local files within | ||
the local package, or dependencies resolved in `node_modules`. | ||
Note that you can _not_ set conditional imports in this way, so | ||
you can't use this to have `#import` style module identifiers | ||
pointing to something outside of the built `dist` folder. (That | ||
_is_ supported with `imports` in the top level `package.json`, | ||
with some caveats. See below.) | ||
To support this, tshy copies the `imports` field from the | ||
project's package.json into these dialect-setting generated | ||
package.json files, and creates symlinks into the appropriate | ||
places so that they resolve to the same files on disk. | ||
None of the keys in `tshy.imports` are allowed to conflict with | ||
the keys in the `package.json`'s top-level `imports`. | ||
Because symlinks may not be included in npm packages (and even if | ||
they are included, they won't be unpacked at install time), the | ||
symlinks it places in `./dist` wouldn't do much good. In order to | ||
work around _this_ restriction, tshy creates a node program at | ||
`dist/.tshy-link-imports.mjs`, which generates the symlinks at | ||
install time via the `preinstall` script. | ||
</details> | ||
_If a `tshy.imports` is present (a previous iteration of this | ||
behavior), it will be merged into the top-level `"imports"` and | ||
deleted from the `tshy` section._ | ||
### Making Noise | ||
@@ -462,44 +507,2 @@ | ||
## Package `#imports` (outside of `tshy` config) | ||
If you use `"imports"` in your package.json, then tshy will set | ||
`scripts.preinstall` to set up some symbolic links to make it | ||
work. This just means you can't use `scripts.preinstall` for | ||
anything else if you use `"imports"`. | ||
<details> | ||
<summary>tl;dr explanation</summary> | ||
The `"imports"` field in package.json allows you to set local | ||
package imports, which have the same kind of conditional import | ||
logic as `"exports"`. This is especially useful when you have a | ||
vendored dependency with `require` and `import` variants, modules | ||
that have to be bundled in different ways for different | ||
environments, or different dependencies for different | ||
environments. | ||
These package imports are _always_ resolved against the nearest | ||
`package.json` file, and tshy uses generated package.json files | ||
to set the module dialect to `"type":"module"` in `dist/esm` and | ||
`"type":"commonjs"` in `dist/commonjs`, and it swaps the | ||
`src/package.json` file between this during the `tsc` builds. | ||
Furthermore, local package imports may not be relative files | ||
outside the package folder. They may only be local files within | ||
the local package, or dependencies resolved in `node_modules`. | ||
To support this, tshy copies the `imports` field from the | ||
project's package.json into these dialect-setting generated | ||
package.json files, and creates symlinks into the appropriate | ||
places so that they resolve to the same files on disk. | ||
Because symlinks may not be included in npm packages (and even if | ||
they are included, they won't be unpacked at install time), the | ||
symlinks it places in `./dist` wouldn't do much good. In order to | ||
work around _this_ restriction, tshy creates a node program at | ||
`dist/.tshy-link-imports.mjs`, which generates the symlinks at | ||
install time via the `preinstall` script. | ||
</details> | ||
## Local Package `exports` | ||
@@ -506,0 +509,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
595
9
136538
115
1058