js-outreach-groups icon indicating copy to clipboard operation
js-outreach-groups copied to clipboard

Tools: Common Conventions

Open hybrist opened this issue 1 year ago • 4 comments

There's a variety of behaviors in authored JavaScript that (web) developers may depend on that aren't described in any officially published spec. This is an attempt to collect them and link to the best possible source for the expected behavior.

Module System

  • Spec deviations
    • Lack of import hoisting (running code before imports in the same file)
    • Different import graph traversal order
    • Conflating default exports and module namespace objects
    • Named exports from JSON imports
  • Implicitly injected imports, e.g. for JSX element and fragment
  • JS-style module resolution inside of CSS, e.g. for url() or @import

Additional module types

  • CommonJS modules
    • CommonJS code inside of ES modules (e.g. require)
    • CommonJS / ES module interop
    • Detection of ES module-generated CommonJS code (__esModule)
  • File content imports (e.g. ./x.txt?raw)
  • File content as data: URL imports (e.g. data-url:./x.txt)
  • Bundle content imports (bundle-text:./foo.css)
  • Side-effect CSS imports, implicitly added to the page
  • .module.css (import CSS classes)
  • Macro imports (https://bun.sh/docs/bundler/macros; https://parceljs.org/features/macros/)

Specifier resolution

  • Symlink vs real path for file system resolution
  • Extension searching & deduping of modules; target-environment-specific suffixes.
  • Node-style resolution of bare specifiers
    • exports field in package.json
    • imports field in package.json
    • <name>/* for package-relative imports ("self-referential import")
  • Exports conditions
    • browser
    • development/production
    • Runtime keys (e.g. node)
    • module
    • require / import
    • module-sync
    • node-addons
  • ~ for package-relative imports
  • source field in package.json
  • browser field in package.json
  • module field in package.json
  • alias field in package.json
  • Deno-style resolution (deno.json#imports)
  • paths option in tsconfig.json
  • npm: scheme in import specifiers
  • node: scheme in import specifiers

Glob imports

  • import.meta.glob (Vite), import.meta.webpackContext/require.context (webpack)
  • import('/files/' + x + '.json')

HMR (Hot Module Replacement)

  • import.meta.hot / import.meta.webpackHot / module.hot, typically tool-specific APIs

Syntax Features

  • TypeScript-style type annotations
  • TypeScript syntax features like enum
  • JSX expressions

Runtime APIs

  • global alias for globalThis
  • process.env
  • fs.readFileSync (replaced with file contents)
  • new WebWorker(new URL('./x.js', import.meta.url), { type: 'module' }) (replaced with bundle with x.js as entry point)
    • Similar for worklets, service worker.

Compilation

  • sideEffects field in package.json
  • compiler-notations-spec
    • /*#__PURE__*/
    • /*#__NO_SIDE_EFFECTS__*/
  • Constant replacement & defines
    • Special case: process.env replacement
    • Special case: process.env.NODE_ENV replacement
    • Special case: import.meta.env replacement
    • Special case: import.meta.env.{DEV,PROD,MODE,SSR} replacement

Configuration

  • .env files for constant definitions
  • browserlists to configure target configuration(s)

Notable omissions

These are things that developers may depend on and that aren't part of official specs - but they aren't provided/polyfilled by tools, generally speaking.

  • Error.prepareStackTrace
  • Error.stack

hybrist avatar Dec 16 '24 22:12 hybrist

Sequence of fields inspected in package.json in order to locate a suitable bundle for use, and also what forms of JS artifacts are acceptable for each field.

Specific example:

  • Webpack 4 apparently looks for ["browser", "main", "module"] fields, in that order ( https://github.com/webpack/webpack/blob/v4.44.2/lib/WebpackOptionsDefaulter.js#L66-L74C18
    • Webpack 4 also apparently does not accept .mjs for "browser" or "main" from what I've seen
    • Webpack 5's logic is more complex, and does seem to accept .mjs files ( https://github.com/webpack/webpack/blob/v5.97.1/lib/config/defaults.js#L1531-L1570 )
  • Metro has its own sequence of fields that it looks at, including "react-native". It also looks for files with a .native.js extension as part of its resolution

I ran into a lot of these issues while updating the Redux packages:

also tagging @aryaemami59 , as this is likely up your alley

markerikson avatar Jan 09 '25 19:01 markerikson

Here are some additional fields I’ve encountered:

Top-Level/Main Fields in package.json

  • unpkg
  • umd
  • jsnext:main (Legacy)
  • jsnext (Legacy)
  • umd:main
  • esnext
  • react-native
  • typings
  • typesVersions

exports conditions

  • source
  • webpack
  • react-native
  • bun
  • deno
  • react-server
  • style
  • sass
  • asset
  • script
  • esmodules
  • electron
  • worker
  • worklet
  • svelte
  • solid

I will add explanations for each of these hopefully soon enough.


Potential source confusion

  • publishConfig: This field allows for overriding certain fields in package.json at during publication. Each package manager can target or leave out certain fields in package.json and it's not always clear which package managers target which fields.
  • directories: Does it actually provide a use-case or is it just meta-data?

aryaemami59 avatar Jan 11 '25 21:01 aryaemami59

For more exports conditions, also see the WinterCG runtime keys proposal.

phryneas avatar Jan 11 '25 21:01 phryneas

(Via @nicolo-ribaudo) ESTree is another internal convention across tools.

hybrist avatar Jan 22 '25 17:01 hybrist