꧁B꧂꧁J꧂꧁O꧂꧁R꧂꧁N꧂
« Website Reddit Twitter Mastodon Sponsor »
꧁L꧂꧁U꧂
Lint packaging errors
Home Page: https://publint.dev
License: MIT License
"exports": {
".": {
"import": {
"default": "./dist/es"
}
},
"./*": {
"import": {
// !: should be ignore
"development": "./src/*",
"default": "./dist/es/*"
}
}
}
error1:
❯ publint
xxx lint results:
Warnings:
1. pkg.exports["./*"].import.development matches ./src/base/create-component.js and is written in ESM, but is interpreted as CJS. Consider using the .mjs extension, e.g. ./src/base/create-component.mjs
error2:
1. pkg.exports["./*"].import.development is ./src/* but the file is not published. Is it specified in pkg.files?
publint
prints errors in package.json
in the format pkg.exports["./path/to/exports"].property has error X
. A minor quality of life improvement would be printing the package.json
path with line numbers such that you can Ctrl+Click to the error in VS Code and friends.
Svelte brings up new a11y warnings for that component, apparently I was using some ARIA attributes wrong 😬
Viewable via VSCode, or when running the site in dev mode.
Would be great to scan jsnext:main
, browser
, types
, exports.*
, etc for missing files.
Having the ability to mark warnings as errors when running publint
through the CLI might be pretty useful. Some lint rules such as types not being properly defined via exports come as warnings and being able to promote them to errors (not just them, but just say we forbid errors and warnings) would be great.
I was thinking about some flags such as --warning-as-error
or --max-warnings num
(see https://eslint.org/docs/latest/use/command-line-interface#max-warnings).
Thx for the awesome library!
Can the users add custom rules and change the severity of the rules for specific packages, by some kind of configuration files?
kinds of like eslint?
It would be great if publint
could check if some dependencies where listed multiple times across dependencies
+ devDependencies
and dependencies
+ peerDependencies
.
I can't actually find any good resource that explains if this is valid or not, but my intuition tells me that it doesn't make sense to have the same package listed multiple times across the different types of dependencies. I might very well be forgetting some use cases though.
typescript
both as a dependency and a dev dependency? If it's a dependency then it will already be there during development, meaning that the devDependency is unnecessarytypescript
both as a dependency and a peer dependency? If it's a dependency then why would you need peers to add the dependency as well?typescript
as a peer dependency and a dev dependency, so it's there during development, but not during production. You could even argue that there should be a rule that checks if all peer dependencies are listed as dev dependencies? (I might be too simple minded here)The reason this comes up for me, is because we're discussing if this is valid or not:
{
"dependencies": {
"typescript": "^4.9.0"
},
"devDependencies": {
},
"peerDependencies": {
"typescript": "^4.0.0 || ^5.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
},
}
One party thinks this means "if TypeScript is supplied as a peer that's great, use that. If not, let the package manager know that it should install it for us", while I believe that this means "always install TypeScript as a dependency, it's an optional peer dependency too but that will never be used anyway".
Maybe there are use cases for having the same package listed with different versions, but I can't personally think of any.
Error occurs when using @vite/release-script node version 18; Also, the project is not type:module
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in E:\workspace\element-plus-xx\node_modules.pnpm@[email protected]\node_modules\publint\package.json
https://publint.bjornlu.com/[email protected]
These files does exist but reported as not exist.
printMessage
returns the string, it sounds more like formatMessage
instead of actually printing. Maybe considering a rename or adding a new API formatMessage
and then deprecate printMessage
in next big release
vite/packages/vite$ pnpx publint deps
pnpm: Command failed with exit code 1: publint deps
at makeError (~/.local/share/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/pnpm.cjs:23105:17)
at handlePromise (~/.local/share/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/pnpm.cjs:23676:33)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Object.handler [as dlx] (~/.local/share/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/pnpm.cjs:215806:7)
at async ~/.local/share/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/pnpm.cjs:223397:21
at async main (~/.local/share/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/pnpm.cjs:223366:34)
at async runPnpm (~/.local/share/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/pnpm.cjs:223596:5)
at async ~/.local/share/pnpm/global/5/.pnpm/[email protected]/node_modules/pnpm/dist/pnpm.cjs:223588:7
Sometimes there is a problem in the new version, but the previous version is no problem, add a select to quickly switch the version to easily locate which version is the problem.
I can try to create a PR if you think it works.。
If both exports
field and browser
field is used, browser
field will map the result of exports
field to another path.
For example, if the following package.json
and a worker condition is used, .
will resolve to ./foo.browser2.js
for most bundlers (except esbuild).
{
"browser": {
"./foo.browser.js": "./foo.browser2.js"
},
"exports": {
".": {
"worker": "./foo.browser.js"
}
}
}
See https://github.com/patdx/package-exports-test.
I believe this is really confusing and it's better to avoid relying on this behavior if it's possible.
That said, I haven't come up with a rewriting procedure that can be done universally. (other than duplicating the file)
Maybe we can give a warning if browser
field maps the path of exports
field? But I don't know if it's possible to avoid in all cases.
So I think showing the final resolved path is a good alternative.
related: solidjs/solid-start#263, vitejs/vite#10323, Andarist/react-textarea-autosize#385
I use the new SvelteKit packaging and when I run publint, I get the following error even though I have the dist/index.js file.
> svelte-kit sync && svelte-package && publint
src/lib -> dist
svelte-awesome-icons lint results:
Errors:
1. pkg.main is index.js but the file does not exist.
What could be the reasons?
We've been having issues with reports about @apollo/client
not working in SvelteKit/Vite.
Publint reports issues with the package: https://publint.dev/@apollo/[email protected]
Cool package! thanks for publishing!
Given this dir structure in a package root, I want to ensure that exports
includes all of those files.
elements
├── pf-accordion
│ ├── pf-accordion-header.js
│ ├── pf-accordion.js
│ └── pf-accordion-panel.js
├── pf-avatar
│ └── pf-avatar.js
├── pf-badge
│ └── pf-badge.js
└── pf-tooltip
└── pf-tooltip.js
20 directories, 26 files
So linting with an option like exportGlobs: ['elements/*/pf-*.js']
,
This "exports"
field should fail
{ '.': './pfe.js' }
and this one should pass:
{
'.': './pfe.js',
'./pf-accordion/pf-accordion-header.js': './pf-accordion/pf-accordion-header.js',
'./pf-accordion/pf-accordion-panel.js': './pf-accordion/pf-accordion-panel.js',
'./pf-accordion/pf-accordion.js': './pf-accordion/pf-accordion.js',
'./pf-avatar/pf-avatar.js': './pf-avatar/pf-avatar.js',
'./pf-badge/pf-badge.js': './pf-badge/pf-badge.js',
'./pf-tooltip/pf-tooltip.js': './pf-tooltip/pf-tooltip.js'
}
The exports
field supersedes other mainFields, if the tooling supports the exports
field.
exports
is supported since:
exports
, which is what the majority still uses.@rollupjs/plugin-node-resolve
v11.1 (Release: 2021-01-15)exports
is still not supported by:
eslint-plugin-node
- import-js/eslint-plugin-import#1810 (new fork should be used instead: https://github.com/eslint-community/eslint-plugin-n)Given the state of support, perhaps we have to wait a bit longer. Because many packages will have both main
/module
and exports
, and suggesting this would be a huge breaking change without much benefit.
I think there's now enough support to start providing this suggestion. There's still the concern that it's totally fine to keep using the main fields just in case, but I think this is a good nudge towards a single way to export things, and will help simplify library publishing in the future.
This suggestion is initially brought up in #21.
Crawling imports can enable linting more files that may have exports issues, e.g. a .mjs
file imports an ESM .js
file but is treated as CJS. This would catch errors for sveltejs/kit#8267.
This would also enable catching packages that uses nodejs builtin modules for browser exports. (NOTE: We may need a setting to tell if this package is from node or browser by default)
There could be typo where import
and require
are flipped, make sure we catch this by analyzing the file syntax to capture the intent (not what it will be interpreted as it's a second issue)
https://publint.bjornlu.com/[email protected]
It stops with "Linting package..." with the error below.
Uncaught (in promise) SyntaxError: Expected property name or '}' in JSON at position 2
Currently npx publint [dir]
only supports linting a single directory. It would be convenient to have a command like npx publint deps
to lint all deps quickly.
Other requirements:
--filter
argument will likely be neededOther ideas:
package.json
on https://publint.dev--cache
argumentExporting types
is hard when you have multiple entrypoints. If the consumer's TypeScript configuration supports node16
, the types
condition should work splendidly. If not, there's a couple ways around it.
If you have an export like my-lib/subpath
, make sure you have a /subpath.d.ts
file. Likewise for my-lib/really/deep/subpath
, create a /really/deep/subpath.d.ts
file.
If your package has an ambient file, you can use declare module
to achieve the same without needing to create new files. The downside though is that all types are in a single file, and the user needs to understand how ambient types work.
declare module 'my-lib/subpath' {
export const hello: string
}
Because it's an ambient file, you will need the user to do /// <reference types="my-lib" />
in their own ambient file.
// src/global.d.ts (common names: env, ambient)
/// <reference types="my-lib" />`
It's not recommended to add to
compilerOptions.types
as that overridestypes
search. (TODO: needs more explanation)
Ambient files must also be script files, not module files. https://www.typescriptlang.org/docs/handbook/modules.html
In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module. Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well).
Ideally we want to make sure types
work in either node16
or not, and we give a hint on the first approach. The second approach is valid and publint needs to check patterns like these to not incorrectly give the hint.
When linting locally, a file could be exported but not specified in the files
field. Make sure we catch this too.
By enabling the upload of arbitrary local build artifacts, those packages that are otherwise published privately could also be linted. I realize that this directly conflicts with the name of the project but I think it would be a very useful feature.
Thank you, awesome project anyway!
The browser
field could be used to replace nodejs files to browser-safe alternative files, but the browser
export condition also covers this area, albeit both have different strategies.
It might be worth recommending using the browser exports condition instead in all cases.
The warnings in https://publint.dev/[email protected] should probably not show up because these are Flow types and not actual JS
Take the following package.json
{
"exports": {
".": {
"require": "./dist/index.cjs",
"import": "./dist/index.js"
}
},
}
It didn't warn about a missing top level types
field or a missing types
field in the exports map, it did however warn about a missing types
field in the exports map when adding a top level types field
The npx publint deps
command could receive a --recursive
option to lint nested deps
Hi!
Linting my project with publint raises a warning: the linter recommend adding a types
exports.
However, if I follow this recommendation, this creates an issue according to a linter of TypeScript team member: "Types are ESM, but implementation is CJS.". The only way to correctly export types when dual publishing cjs and esm is to remove the types
exports and adding .d.cts
/ .d.ts
/ .d.mts
along with corresponding files.
I understand the reasoning behind requiring types
exports.
publint
could detect dual publishing and then, in this particular case, recommend removing types
exports.
typesVersion
remaps the types
path and should be taken into account when checking if the file exists.
Support npx publint ./packages/*
to lint all directories at once, and only those that have a package.json
.
Make sure this works with --deps
too, which would create another dimension when linting. (N directories x N deps)
I may be wrong and this is necessary, but I've seen the second setup in a popular open source library - is it invalid or not?
"types": "./src/types.d.ts",
"exports": {
"./package.json": "./package.json",
".": {
"import": {
"types": "./src/types.d.mts",
"default": "./dist/file.mjs"
},
"module": {
"types": "./src/types.d.ts",
"default": "./dist/file.esm.js"
},
"default": {
"types": "./src/types.d.ts",
"default": "./dist/file.umd.js"
}
}
},
- pkg.exports["."].types types is an invalid format when resolving with the "import" condition. Consider splitting out two types conditions for import and require, and use the .mts extension, e.g. pkg.exports["."].import.types: "./src/types.d.mts"
"types": "./src/types.d.ts",
"exports": {
"./package.json": "./package.json",
".": {
"types": "./src/types.d.ts",
"import": {
"types": "./src/types.d.mts",
"default": "./dist/file.mjs"
},
"module": "./dist/file.esm.js",
"default": "./dist/file.umd.js"
}
},
This is a nice tool 💙
subpath folder mappings are not supported by Vite.
https://vitejs.dev/config/#resolve-conditions:~:text=Resolving%20subpath%20exports,subpath%20patterns%20instead.
Also it is deprecated. (and is removed in 17.x+?)
https://nodejs.org/docs/latest-v16.x/api/packages.html#subpath-folder-mappings
Example package: https://publint.bjornlu.com/@ringcentral/[email protected]
I want to integrate this into https://github.com/vitejs/release-scripts via the JS API and it would be nice to expose some of the code use in the CLI to simplify integration there.
When linting a package, the package could be meant for browsers only, or nodejs only. This isn't statically anaylzable (though possibly inferred), so it may need a new configuration option to specify the intended environment.
This would make things less "automatic" but is needed for correctness. I'm not sure yet how this would work with npx publint deps
and the site.
For example, fs
package is not shown in search but exists.
Currently publint shows this as not found: https://publint.bjornlu.com/fs
Then printMessage
requires two arguments, message and package json, but document is missing pkg
argument.
Another is printMessage
returns the string, it sounds more like formatMessage
instead of actually printing. Maybe considering a rename or adding a new API formatMessage
and then deprecate printMessage
in next big release
I usually use npm pack
to test files locally . That has resulted in the tarball install path ending up in a published package before. It would be nice if Publint had a check guarding against packages installed from local files:
"dependencies": {
"package": "file:../package-0.1.0.tgz"
}
I found that you MUST prefix the paths with ./
or they'll fail with esbuild, not sure if this happens with other bundlers or environments, e.g.
"exports": {
".": {
"import": "dist/somedom.mjs",
"require": "dist/somedom.js",
"browser": "dist/somedom.umd.js"
},
"./ssr": {
"import": "dist/index.ssr.mjs",
"require": "dist/index.ssr.js"
}
}
This configuration yields The package target "dist/somedom.mjs" is invalid
if consumed on other modules.
Once I prefixed the paths to ./dist
then it worked without issues.
I think this is an issue, because I ran publint
and it said it was correct, which was not true.
So much thanks for this amazing tool, see you!
npm v9 introduced a breaking change to npm-packlist
(release notes). However yarn v3 and pnpm v7 have not adopted this yet.
This issue tracks when yarn and pnpm start moving towards the new pack logic before we update it.
"exports": {
"require": {
"types": "..."
},
"types": "..."
}
This is a valid configuration, but publint
sees require
before types
shallowly and naively that it reports a false error.
https://publint.dev/@hebilicious/[email protected]
https://twitter.com/its_hebilicious/status/1674316686269497348
The "module"
exports condition can use any extension regardless of the code format (ESM or CJS) as nodejs ignores the condition by default. Similarly to the "exports"
field, it's used by bundlers only so the extension restriction doesn't apply here.
I don't know if it happens often, but lightningcss is green on publint but fails to check with TS 5 bundler mode:
TS7016: Could not find a declaration file for module 'lightningcss'. '/Users/arnaud/git/rds/node_modules/lightningcss/node/index.mjs' implicitly has an 'any' type.
There are types at '/Users/arnaud/git/rds/node_modules/lightningcss/node/index.d.ts', but this result could not be resolved when respecting package.json "exports". The 'lightningcss' library may need to update its package.json or typings.
Following #27, we need to handled the case where there's multiple *
in the key.
We also need to handle the case where there's a *
in the key, but no *
in the value. (All subpath matches one file)
https://stackblitz.com/edit/node-lzjbma?file=packages%2Fapp%2Findex.js
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.