├── mock ├── tls.js ├── empty.js ├── tty.js ├── net.js ├── buffer.js ├── punycode.js ├── console.js ├── dns.js └── process.js ├── example ├── esm │ ├── browserify.mjs │ ├── rollup.html │ ├── esbuild.html │ ├── webpack.html │ ├── browserify.html │ ├── vite │ │ ├── index.html │ │ └── vite.config.mjs │ ├── esbuild.mjs │ ├── webpack.config.mjs │ ├── rollup.config.mjs │ └── index.mjs ├── cjs │ ├── esbuild.html │ ├── rollup.html │ ├── webpack.html │ ├── browserify.html │ ├── vite │ │ ├── index.html │ │ └── vite.config.js │ ├── webpack.config.js │ ├── browserify.js │ ├── esbuild.js │ ├── rollup.config.js │ └── index.js ├── README.md └── package.json ├── .npmrc ├── .browserslistrc ├── test ├── .eslintrc └── index.js ├── .huskyrc ├── proxy ├── process │ └── browser.js ├── querystring.js ├── process.js └── url.js ├── .nycrc ├── helpers ├── esbuild │ ├── shim.src.js │ └── plugin.js ├── webpack │ └── plugin.js └── rollup │ └── plugin.js ├── .lintstagedrc ├── types └── lib.d.ts ├── .editorconfig ├── .gitignore ├── .babelrc ├── .prettierrc ├── tsconfig.json ├── .github └── workflows │ └── ci.yml ├── LICENSE.md ├── .eslintrc ├── CHANGELOG.md ├── index.js ├── rollup.config.js ├── package.json └── README.md /mock/tls.js: -------------------------------------------------------------------------------- 1 | // Todo 2 | -------------------------------------------------------------------------------- /example/esm/browserify.mjs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /mock/empty.js: -------------------------------------------------------------------------------- 1 | export default null; 2 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | last 3 major versions 2 | since 2019 3 | not ie <= 10 4 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["eslint-config-niksy/tests"] 3 | } 4 | -------------------------------------------------------------------------------- /.huskyrc: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "pre-commit": "lint-staged" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /mock/tty.js: -------------------------------------------------------------------------------- 1 | function noop() {} 2 | 3 | export { noop as isatty, noop as setRawMode }; 4 | -------------------------------------------------------------------------------- /proxy/process/browser.js: -------------------------------------------------------------------------------- 1 | import api from '../process.js'; 2 | 3 | export default api; 4 | 5 | export * from '../process.js'; 6 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "statements": 80, 3 | "lines": 0, 4 | "reporter": ["html", "text"], 5 | "sourceMap": false, 6 | "instrument": false 7 | } 8 | -------------------------------------------------------------------------------- /helpers/esbuild/shim.src.js: -------------------------------------------------------------------------------- 1 | import { Buffer } from 'buffer'; 2 | import process from 'process'; 3 | 4 | const _global = globalThis; 5 | 6 | export { Buffer, process, _global as global }; 7 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "*.js": ["eslint --fix"], 3 | "*.(md|json|yml)": ["prettier --ignore-path .gitignore --write"], 4 | ".!(npm|browserslist)*rc": [ 5 | "prettier --ignore-path .gitignore --parser json --write" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /mock/net.js: -------------------------------------------------------------------------------- 1 | function noop() {} 2 | function bool() { 3 | return true; 4 | } 5 | 6 | export { 7 | noop as createServer, 8 | noop as createConnection, 9 | noop as connect, 10 | bool as isIP, 11 | bool as isIPv4, 12 | bool as isIPv6 13 | }; 14 | -------------------------------------------------------------------------------- /mock/buffer.js: -------------------------------------------------------------------------------- 1 | function Buffer() { 2 | throw new Error('Buffer is not included.'); 3 | } 4 | Buffer.isBuffer = function () { 5 | return false; 6 | }; 7 | 8 | const INSPECT_MAX_BYTES = 50; 9 | 10 | export { INSPECT_MAX_BYTES, Buffer as SlowBuffer, Buffer }; 11 | -------------------------------------------------------------------------------- /types/lib.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'querystring-es3' { 2 | import { decode, encode, parse, stringify } from 'querystring'; 3 | export { decode, encode, parse, stringify }; 4 | }; 5 | 6 | declare module 'process/browser.js' { 7 | const process: NodeJS.Process; 8 | export = process; 9 | }; 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [{package.json,*.yml}] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | package-lock.json 4 | yarn.lock 5 | npm-debug.log 6 | .nyc_output/ 7 | coverage/ 8 | cjs/ 9 | esm/ 10 | !test/fixtures/node_modules/ 11 | !example/cjs 12 | !example/esm 13 | example/cjs/*.dist.js 14 | example/cjs/vite/dist 15 | example/esm/*.dist.js 16 | example/esm/vite/dist 17 | helpers/esbuild/shim.js 18 | -------------------------------------------------------------------------------- /example/cjs/esbuild.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | esbuild, CommonJS 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/cjs/rollup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Rollup, CommonJS 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/cjs/webpack.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Webpack, CommonJS 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/esm/rollup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Rollup, ES Modules 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/esm/esbuild.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | esbuild, ES Modules 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/esm/webpack.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Webpack, ES Modules 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/cjs/browserify.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Browserify, CommonJS 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/esm/browserify.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Browserify, ES Modules 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/esm/vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite, ES Modules 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/cjs/vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite, CommonJS 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /mock/punycode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} s 3 | */ 4 | function passthrough(s) { 5 | return s; 6 | } 7 | 8 | const ucs2 = { 9 | encode: passthrough, 10 | decode: passthrough 11 | }; 12 | 13 | const version = '0.0.0'; 14 | 15 | export { 16 | ucs2, 17 | version, 18 | passthrough as encode, 19 | passthrough as decode, 20 | passthrough as toUnicode, 21 | passthrough as toASCII 22 | }; 23 | -------------------------------------------------------------------------------- /helpers/webpack/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const webpack = require('webpack'); 4 | 5 | const nodeProtocolRegex = /^node:/; 6 | 7 | function NodeProtocolUrlPlugin() { 8 | return new webpack.NormalModuleReplacementPlugin( 9 | nodeProtocolRegex, 10 | (resource) => { 11 | resource.request = resource.request.replace(nodeProtocolRegex, ''); 12 | } 13 | ); 14 | } 15 | 16 | module.exports.NodeProtocolUrlPlugin = NodeProtocolUrlPlugin; 17 | -------------------------------------------------------------------------------- /mock/console.js: -------------------------------------------------------------------------------- 1 | const _console = globalThis.console ?? {}; 2 | 3 | const consoleApi = { 4 | log: 1, 5 | info: 1, 6 | error: 1, 7 | warn: 1, 8 | dir: 1, 9 | trace: 1, 10 | assert: 1, 11 | time: 1, 12 | timeEnd: 1 13 | }; 14 | 15 | /** @typedef {keyof consoleApi} ConsoleApi */ 16 | 17 | for (const property in consoleApi) { 18 | if (!_console[/** @type {ConsoleApi} */ (property)]) { 19 | _console[/** @type {ConsoleApi} */ (property)] = function () {}; 20 | } 21 | } 22 | 23 | export default _console; 24 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "modules": false, 7 | "loose": true 8 | } 9 | ] 10 | ], 11 | "plugins": ["babel-plugin-transform-globalthis"], 12 | "overrides": [ 13 | { 14 | "test": ["./index.js", "./test/index.js"], 15 | "presets": [ 16 | [ 17 | "@babel/preset-env", 18 | { 19 | "modules": false, 20 | "loose": true, 21 | "targets": { 22 | "node": "10" 23 | } 24 | } 25 | ] 26 | ] 27 | } 28 | ], 29 | "env": { 30 | "test": { 31 | "plugins": ["babel-plugin-istanbul"] 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /mock/dns.js: -------------------------------------------------------------------------------- 1 | /* globals unknown */ 2 | 3 | /** 4 | * @param {unknown[]} arguments_ 5 | */ 6 | const api = function (...arguments_) { 7 | if (arguments_.length === 0) { 8 | return; 9 | } 10 | const callback = arguments_[arguments_.length - 1]; 11 | if (typeof callback === 'function') { 12 | callback(null, '0.0.0.0'); 13 | } 14 | }; 15 | 16 | export { 17 | api as lookup, 18 | api as resolve4, 19 | api as resolve6, 20 | api as resolveCname, 21 | api as resolveMx, 22 | api as resolveNs, 23 | api as resolveTxt, 24 | api as resolveSrv, 25 | api as resolveNaptr, 26 | api as reverse, 27 | api as resolve 28 | }; 29 | -------------------------------------------------------------------------------- /example/esm/vite/vite.config.mjs: -------------------------------------------------------------------------------- 1 | import { createRequire } from 'module'; 2 | import inject from '@rollup/plugin-inject'; 3 | import stdLibBrowser from '../../../esm/index.js'; 4 | 5 | const require = createRequire(import.meta.url); 6 | const esbuildShim = require.resolve('../../../helpers/esbuild/shim'); 7 | 8 | export default { 9 | resolve: { 10 | alias: stdLibBrowser 11 | }, 12 | optimizeDeps: { 13 | include: ['buffer', 'process'] 14 | }, 15 | plugins: [ 16 | { 17 | ...inject({ 18 | global: [esbuildShim, 'global'], 19 | process: [esbuildShim, 'process'], 20 | Buffer: [esbuildShim, 'Buffer'] 21 | }), 22 | enforce: 'post' 23 | } 24 | ] 25 | }; 26 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 4, 4 | "useTabs": true, 5 | "semi": true, 6 | "singleQuote": true, 7 | "quoteProps": "preserve", 8 | "jsxSingleQuote": true, 9 | "trailingComma": "none", 10 | "bracketSpacing": true, 11 | "bracketSameLine": false, 12 | "arrowParens": "always", 13 | "requirePragma": false, 14 | "insertPragma": false, 15 | "proseWrap": "always", 16 | "htmlWhitespaceSensitivity": "css", 17 | "vueIndentScriptAndStyle": false, 18 | "endOfLine": "lf", 19 | "embeddedLanguageFormatting": "auto", 20 | "overrides": [ 21 | { 22 | "files": ["package.json", "*.yml"], 23 | "options": { 24 | "useTabs": false, 25 | "tabWidth": 2 26 | } 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /example/cjs/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const { NodeProtocolUrlPlugin } = require('../../helpers/webpack/plugin.js'); 4 | const stdLibraryBrowser = require('../../cjs/index.js'); 5 | 6 | module.exports = { 7 | mode: 'none', 8 | entry: './cjs/index.js', 9 | output: { 10 | library: 'stdLibBrowser', 11 | libraryTarget: 'umd', 12 | filename: 'webpack.dist.js', 13 | path: __dirname 14 | }, 15 | resolve: { 16 | alias: stdLibraryBrowser 17 | }, 18 | plugins: [ 19 | new NodeProtocolUrlPlugin(), 20 | new webpack.ProvidePlugin({ 21 | process: stdLibraryBrowser.process, 22 | Buffer: stdLibraryBrowser.buffer 23 | }) 24 | ] 25 | }; 26 | -------------------------------------------------------------------------------- /example/cjs/browserify.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const browserify = require('browserify'); 4 | const aliasify = require('aliasify'); 5 | const stdLibBrowser = require('../../cjs/index.js'); 6 | 7 | const b = browserify([path.resolve(__dirname, 'index.js')], { 8 | standalone: 'stdLibBrowser', 9 | transform: [[aliasify, { aliases: stdLibBrowser }]], 10 | insertGlobalVars: { 11 | process: () => { 12 | return `require('${stdLibBrowser.process}')`; 13 | }, 14 | Buffer: () => { 15 | return `require('${stdLibBrowser.buffer}').Buffer`; 16 | } 17 | } 18 | }); 19 | 20 | b.bundle().pipe( 21 | fs.createWriteStream(path.resolve(__dirname, 'browserify.dist.js')) 22 | ); 23 | -------------------------------------------------------------------------------- /example/cjs/vite/vite.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/dynamic-import-chunkname */ 2 | 3 | const inject = require('@rollup/plugin-inject'); 4 | 5 | const esbuildShim = require.resolve('../../../helpers/esbuild/shim'); 6 | 7 | module.exports = async () => { 8 | const { default: stdLibBrowser } = await import('../../../esm/index.js'); 9 | return { 10 | resolve: { 11 | alias: stdLibBrowser 12 | }, 13 | optimizeDeps: { 14 | include: ['buffer', 'process'] 15 | }, 16 | plugins: [ 17 | { 18 | ...inject({ 19 | global: [esbuildShim, 'global'], 20 | process: [esbuildShim, 'process'], 21 | Buffer: [esbuildShim, 'Buffer'] 22 | }), 23 | enforce: 'post' 24 | } 25 | ] 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "lib": ["ESNext", "DOM"], 6 | "moduleResolution": "node", 7 | "allowSyntheticDefaultImports": true, 8 | "esModuleInterop": true, 9 | "resolveJsonModule": true, 10 | "noEmit": true, 11 | "newLine": "lf", 12 | "strict": true, 13 | "noPropertyAccessFromIndexSignature": true, 14 | "noEmitOnError": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "allowJs": true, 17 | "checkJs": true, 18 | "skipLibCheck": true 19 | }, 20 | "include": [ 21 | "index.js", 22 | "lib/**/*.js", 23 | "proxy/**/*.js", 24 | "mock/**/*.js", 25 | "test/**/*.js", 26 | "types/**/*.ts", 27 | "helpers/**/plugin.js" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /example/cjs/esbuild.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const esbuild = require('esbuild'); 3 | const plugin = require('../../helpers/esbuild/plugin.js'); 4 | const stdLibBrowser = require('../../cjs/index.js'); 5 | 6 | (async () => { 7 | try { 8 | await esbuild.build({ 9 | entryPoints: [path.resolve(__dirname, 'index.js')], 10 | outfile: path.resolve(__dirname, 'esbuild.dist.js'), 11 | bundle: true, 12 | format: 'iife', 13 | globalName: 'stdLibBrowser', 14 | inject: [path.resolve(__dirname, '../../helpers/esbuild/shim.js')], 15 | define: { 16 | global: 'global', 17 | process: 'process', 18 | Buffer: 'Buffer' 19 | }, 20 | plugins: [plugin(stdLibBrowser)] 21 | }); 22 | } catch (error) { 23 | // Handled 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | Test: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | node-version: 11 | - 10 12 | - 12 13 | - 16 14 | steps: 15 | - name: Clone repository 16 | uses: actions/checkout@v2 17 | 18 | - name: Use Node ${{ matrix.node-version }} 19 | uses: actions/setup-node@v2 20 | with: 21 | node-version: ${{ matrix.node-version }} 22 | 23 | - name: Install dependencies 24 | run: npm install 25 | 26 | - name: Lint 27 | run: npm run lint 28 | 29 | - name: Check types 30 | run: npm run lint:types 31 | 32 | - name: Test 33 | run: npm test 34 | -------------------------------------------------------------------------------- /proxy/querystring.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {import('querystring').escape} qsEscape 3 | * @typedef {import('querystring').unescape} qsUnescape 4 | */ 5 | 6 | import { decode, encode, parse, stringify } from 'querystring-es3'; 7 | 8 | /** 9 | * @type {qsEscape} 10 | */ 11 | function qsEscape(string) { 12 | return encodeURIComponent(string); 13 | } 14 | 15 | /** 16 | * @type {qsUnescape} 17 | */ 18 | function qsUnescape(string) { 19 | return decodeURIComponent(string); 20 | } 21 | 22 | const api = { 23 | decode, 24 | encode, 25 | parse, 26 | stringify, 27 | escape: qsEscape, 28 | unescape: qsUnescape 29 | }; 30 | 31 | export default api; 32 | 33 | export { 34 | decode, 35 | encode, 36 | parse, 37 | stringify, 38 | qsEscape as escape, 39 | qsUnescape as unescape 40 | }; 41 | -------------------------------------------------------------------------------- /example/esm/esbuild.mjs: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { fileURLToPath } from 'url'; 3 | import esbuild from 'esbuild'; 4 | import plugin from '../../helpers/esbuild/plugin.js'; 5 | import stdLibBrowser from '../../esm/index.js'; 6 | 7 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 8 | 9 | (async () => { 10 | try { 11 | await esbuild.build({ 12 | entryPoints: [path.resolve(__dirname, 'index.mjs')], 13 | outfile: path.resolve(__dirname, 'esbuild.dist.js'), 14 | bundle: true, 15 | format: 'iife', 16 | globalName: 'stdLibBrowser', 17 | inject: [path.resolve(__dirname, '../../helpers/esbuild/shim.js')], 18 | define: { 19 | global: 'global', 20 | process: 'process', 21 | Buffer: 'Buffer' 22 | }, 23 | plugins: [plugin(stdLibBrowser)] 24 | }); 25 | } catch (error) { 26 | // Handled 27 | } 28 | })(); 29 | -------------------------------------------------------------------------------- /example/esm/webpack.config.mjs: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { fileURLToPath } from 'url'; 3 | import webpack from 'webpack'; 4 | import { NodeProtocolUrlPlugin } from '../../helpers/webpack/plugin.js'; 5 | import stdLibBrowser from '../../esm/index.js'; 6 | 7 | export default { 8 | mode: 'none', 9 | entry: './esm/index.mjs', 10 | output: { 11 | library: 'stdLibBrowser', 12 | libraryTarget: 'umd', 13 | filename: 'webpack.dist.js', 14 | path: path.dirname(fileURLToPath(import.meta.url)) 15 | }, 16 | resolve: { 17 | alias: stdLibBrowser 18 | }, 19 | module: { 20 | rules: [ 21 | { 22 | test: /\.m?js$/, 23 | resolve: { 24 | fullySpecified: false 25 | } 26 | } 27 | ] 28 | }, 29 | plugins: [ 30 | new NodeProtocolUrlPlugin(), 31 | new webpack.ProvidePlugin({ 32 | process: stdLibBrowser.process, 33 | Buffer: stdLibBrowser.buffer 34 | }) 35 | ] 36 | }; 37 | -------------------------------------------------------------------------------- /helpers/rollup/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @typedef {import('rollup')} rollup 5 | * @typedef {import('rollup').WarningHandlerWithDefault} rollup.WarningHandlerWithDefault 6 | */ 7 | 8 | /** 9 | * @type {rollup.WarningHandlerWithDefault} 10 | */ 11 | function handleCircularDependancyWarning(warning, warningHandler) { 12 | const packagesWithCircularDependencies = [ 13 | 'util/', 14 | 'assert/', 15 | 'readable-stream/', 16 | 'crypto-browserify/' 17 | ]; 18 | if ( 19 | !( 20 | warning.code === 'CIRCULAR_DEPENDENCY' && 21 | packagesWithCircularDependencies.some((modulePath) => { 22 | if (typeof warning.importer !== 'string') { 23 | return false; 24 | } 25 | return warning.importer.includes(modulePath); 26 | }) 27 | ) 28 | ) { 29 | warningHandler(warning); 30 | } 31 | } 32 | 33 | module.exports.handleCircularDependancyWarning = 34 | handleCircularDependancyWarning; 35 | -------------------------------------------------------------------------------- /example/esm/rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import json from '@rollup/plugin-json'; 4 | import alias from '@rollup/plugin-alias'; 5 | import inject from '@rollup/plugin-inject'; 6 | import stdLibBrowser from '../../esm/index.js'; 7 | import { handleCircularDependancyWarning } from '../../helpers/rollup/plugin.js'; 8 | 9 | export default { 10 | input: './esm/index.mjs', 11 | output: [ 12 | { 13 | file: 'esm/rollup.dist.js', 14 | format: 'umd', 15 | name: 'stdLibBrowser', 16 | exports: 'auto', 17 | sourcemap: 'inline' 18 | } 19 | ], 20 | plugins: [ 21 | alias({ 22 | entries: stdLibBrowser 23 | }), 24 | resolve({ 25 | browser: true 26 | }), 27 | commonjs(), 28 | json(), 29 | inject({ 30 | process: stdLibBrowser.process, 31 | Buffer: [stdLibBrowser.buffer, 'Buffer'] 32 | }) 33 | ], 34 | onwarn: (warning, rollupWarn) => { 35 | handleCircularDependancyWarning(warning, rollupWarn); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /example/cjs/rollup.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { default: resolve } = require('@rollup/plugin-node-resolve'); 4 | const commonjs = require('@rollup/plugin-commonjs'); 5 | const json = require('@rollup/plugin-json'); 6 | const alias = require('@rollup/plugin-alias'); 7 | const inject = require('@rollup/plugin-inject'); 8 | const stdLibBrowser = require('../../cjs/index.js'); 9 | const { 10 | handleCircularDependancyWarning 11 | } = require('../../helpers/rollup/plugin.js'); 12 | 13 | module.exports = { 14 | input: './cjs/index.js', 15 | output: [ 16 | { 17 | file: 'cjs/rollup.dist.js', 18 | format: 'umd', 19 | name: 'stdLibBrowser', 20 | exports: 'auto', 21 | sourcemap: 'inline' 22 | } 23 | ], 24 | plugins: [ 25 | alias({ 26 | entries: stdLibBrowser 27 | }), 28 | resolve({ 29 | browser: true 30 | }), 31 | commonjs(), 32 | json(), 33 | inject({ 34 | process: stdLibBrowser.process, 35 | Buffer: [stdLibBrowser.buffer, 'Buffer'] 36 | }) 37 | ], 38 | onwarn: (warning, rollupWarn) => { 39 | handleCircularDependancyWarning(warning, rollupWarn); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /helpers/esbuild/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @typedef {import('../../')} Packages 5 | * @typedef {import('esbuild').Plugin} esbuild.Plugin 6 | */ 7 | 8 | const { promisify } = require('util'); 9 | const browserResolve = require('browser-resolve'); 10 | 11 | const pBrowserResolve = promisify(browserResolve); 12 | 13 | const plugin = ( 14 | /** @type {Packages | {[key: string]: string}} */ stdLibBrowser 15 | ) => { 16 | /** @type {esbuild.Plugin} */ 17 | const main = { 18 | name: 'node-stdlib-browser-alias', 19 | async setup(build) { 20 | const map = new Map(); 21 | const promises = Object.entries(stdLibBrowser).map( 22 | async ([name, path]) => { 23 | // @ts-ignore 24 | const resolvedPath = await pBrowserResolve(path, {}); 25 | map.set(name, resolvedPath); 26 | } 27 | ); 28 | await Promise.all(promises); 29 | 30 | map.forEach((path, name) => { 31 | build.onResolve({ filter: new RegExp(`^${name}$`) }, () => { 32 | return { 33 | path 34 | }; 35 | }); 36 | }); 37 | } 38 | }; 39 | return main; 40 | }; 41 | 42 | module.exports = plugin; 43 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) Ivan Nikolić Copyright (c) Tobias Koppers 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "eslint-config-niksy", 5 | "eslint-config-niksy/typescript", 6 | "eslint-config-niksy/next", 7 | "eslint-config-prettier" 8 | ], 9 | "plugins": ["eslint-plugin-prettier", "eslint-plugin-unicorn"], 10 | "globals": { 11 | "globalThis": false 12 | }, 13 | "settings": { 14 | "jsdoc": { 15 | "preferredTypes": ["esbuild"] 16 | } 17 | }, 18 | "rules": { 19 | "prettier/prettier": 1, 20 | "camelcase": 0, 21 | "unicorn/prefer-flat-map": 0, 22 | "unicorn/prevent-abbreviations": [ 23 | 1, 24 | { 25 | "allowList": { 26 | "stdLibBrowser": true 27 | } 28 | } 29 | ] 30 | }, 31 | "ignorePatterns": ["helpers/esbuild/shim.js", "example/**/*"], 32 | "overrides": [ 33 | { 34 | "files": "mock/*.js", 35 | "env": { 36 | "browser": true 37 | } 38 | }, 39 | { 40 | "files": ["rollup.config.js", "helpers/**/plugin.js"], 41 | "parserOptions": { 42 | "sourceType": "script" 43 | }, 44 | "rules": { 45 | "no-console": 0 46 | } 47 | }, 48 | { 49 | "files": ["rollup.config.js", "test/index.js"], 50 | "plugins": ["eslint-plugin-unicorn"], 51 | "rules": { 52 | "unicorn/numeric-separators-style": 0 53 | } 54 | } 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /mock/process.js: -------------------------------------------------------------------------------- 1 | /* globals unknown */ 2 | 3 | import path from 'path'; 4 | 5 | function noop() {} 6 | /** 7 | * @param {unknown[]} arguments_ 8 | */ 9 | function nextTick(...arguments_) { 10 | const [function_] = arguments_; 11 | arguments_.shift(); 12 | setTimeout(function () { 13 | if (typeof function_ === 'function') { 14 | function_.apply(null, arguments_); 15 | } 16 | }, 0); 17 | } 18 | 19 | /** 20 | * @param {unknown} name 21 | */ 22 | function binding(name) { 23 | throw new Error('No such module. (Possibly not yet loaded)'); 24 | } 25 | 26 | const features = {}; 27 | const platformName = 'browser'; 28 | const pid = 1; 29 | const browser = true; 30 | const environment = {}; 31 | /** @type {string[]} */ 32 | const argv = []; 33 | 34 | let cwd = '/'; 35 | function getCwd() { 36 | return cwd; 37 | } 38 | /** 39 | * @param {string} dir 40 | */ 41 | function getChdir(dir) { 42 | cwd = path.resolve(dir, cwd); 43 | } 44 | 45 | export { 46 | features, 47 | nextTick, 48 | pid, 49 | browser, 50 | environment as env, 51 | argv, 52 | binding, 53 | getCwd as cwd, 54 | getChdir as chdir, 55 | noop as exit, 56 | noop as kill, 57 | noop as umask, 58 | noop as dlopen, 59 | noop as uptime, 60 | noop as memoryUsage, 61 | noop as uvCounters, 62 | platformName as platform, 63 | platformName as arch, 64 | platformName as execPath, 65 | platformName as title 66 | }; 67 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Bundler output test 2 | 3 | This directory servers as minimal reproducible configuration for build with 4 | various bundlers. 5 | 6 | Run `npm install && npm run build`, and then: 7 | 8 | - `npm run build:webpack:cjs` - Run Webpack config as CommonJS module 9 | - `npm run build:rollup:cjs` - Run Rollup config as CommonJS module 10 | - `npm run build:esbuild:cjs` - Run esbuild build as CommonJS module 11 | - `npm run build:browserify:cjs` - Run Browserify build as CommonJS module 12 | - `npm run build:webpack:esm` - Run Rollup config as ES module 13 | - `npm run build:rollup:esm` - Run Rollup config as ES module 14 | - `npm run build:esbuild:esm` - Run esbuild build as ES module 15 | - `npm run build:browserify:esm` - Run Browserify build as ES module 16 | - `npm run open:webpack:cjs` - Open generated bundle from Webpack CommonJS 17 | configuration 18 | - `npm run open:rollup:cjs` - Open generated bundle from Rollup CommonJS 19 | configuration 20 | - `npm run open:esbuild:cjs` - Open generated bundle from esbuild CommonJS 21 | build 22 | - `npm run open:browserify:cjs` - Open generated bundle from Browserify 23 | CommonJS build 24 | - `npm run open:webpack:esm` - Open generated bundle from Webpack ES module 25 | configuration 26 | - `npm run open:rollup:esm` - Open generated bundle from Rollup ES module 27 | configuration 28 | - `npm run open:esbuild:esm` - Open generated bundle from esbuild ES module 29 | build 30 | - `npm run open:browserify:esm` - Open generated bundle from Browserify ES 31 | module build 32 | 33 | In each opened HTML file you can see each module output inside browser console. 34 | Each module is accessible from `stdLibBrowser` global property. 35 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased][] 4 | 5 | ## [1.3.1][] - 2025-02-04 6 | 7 | ### Changed 8 | 9 | - Update `crypto-browserify` 10 | ([#41](https://github.com/niksy/node-stdlib-browser/pull/41)) 11 | 12 | ## [1.3.0][] - 2024-11-21 13 | 14 | ### Changed 15 | 16 | - Pin `domain-browser` to 4.22.0 17 | - Add `require` check for path resolver 18 | - Proxy `process` with additional exports 19 | ([#34](https://github.com/niksy/node-stdlib-browser/pull/34)) 20 | 21 | ## [1.2.1][] - 2024-09-16 22 | 23 | ### Added 24 | 25 | - Support information and instructions for Vite 26 | - Update dependencies 27 | 28 | ## [1.2.0][] - 2021-12-08 29 | 30 | ### Added 31 | 32 | - Support for `node:` protocol in Webpack 33 | ([#12](https://github.com/niksy/node-stdlib-browser/issues/12)) 34 | - Rollup warning helper function 35 | 36 | ## [1.1.0][] - 2021-11-16 37 | 38 | ### Added 39 | 40 | - Note regarding `URL` and `URLSearchParams` polyfilling 41 | - Support for esbuild 42 | 43 | ### Changed 44 | 45 | - Improve Rollup global variable injecting 46 | - Use `path.resolve` for URL methods 47 | 48 | ### Fixed 49 | 50 | - `url.format` to handle `URL` 51 | 52 | ## [1.0.0][] - 2021-11-13 53 | 54 | ### Added 55 | 56 | - Initial implementation 57 | 58 | [1.0.0]: https://github.com/niksy/node-stdlib-browser/tree/v1.0.0 59 | [1.1.0]: https://github.com/niksy/node-stdlib-browser/tree/v1.1.0 60 | [1.2.0]: https://github.com/niksy/node-stdlib-browser/tree/v1.2.0 61 | [Unreleased]: https://github.com/niksy/node-stdlib-browser/compare/v1.3.1...HEAD 62 | [1.3.1]: https://github.com/niksy/node-stdlib-browser/compare/v1.3.0...v1.3.1 63 | [1.3.0]: https://github.com/niksy/node-stdlib-browser/compare/v1.2.1...v1.3.0 64 | [1.2.1]: https://github.com/niksy/node-stdlib-browser/tree/v1.2.1 65 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "cd .. && npm run build", 4 | "build:webpack:cjs": "webpack --config ./cjs/webpack.config.js", 5 | "build:webpack:esm": "webpack --config ./esm/webpack.config.mjs", 6 | "build:rollup:cjs": "rollup --config ./cjs/rollup.config.js", 7 | "build:rollup:esm": "rollup --config ./esm/rollup.config.mjs", 8 | "build:esbuild:cjs": "node ./cjs/esbuild.js", 9 | "build:esbuild:esm": "node ./esm/esbuild.mjs", 10 | "build:browserify:cjs": "node ./cjs/browserify.js", 11 | "build:browserify:esm": "node ./esm/browserify.mjs", 12 | "build:vite:cjs": "vite build ./cjs/vite", 13 | "build:vite:esm": "vite build ./esm/vite", 14 | "open:webpack:cjs": "open ./cjs/webpack.html", 15 | "open:webpack:esm": "open ./esm/webpack.html", 16 | "open:rollup:cjs": "open ./cjs/rollup.html", 17 | "open:rollup:esm": "open ./esm/rollup.html", 18 | "open:esbuild:cjs": "open ./cjs/esbuild.html", 19 | "open:esbuild:esm": "open ./esm/esbuild.html", 20 | "open:browserify:cjs": "open ./cjs/browserify.html", 21 | "open:browserify:esm": "open ./esm/browserify.html", 22 | "open:vite:cjs": "vite preview ./cjs/vite --open", 23 | "open:vite:esm": "vite preview ./esm/vite --open" 24 | }, 25 | "devDependencies": { 26 | "@chialab/esbuild-plugin-alias": "^0.12.31", 27 | "@rollup/plugin-alias": "^3.1.5", 28 | "@rollup/plugin-commonjs": "^20.0.0", 29 | "@rollup/plugin-inject": "^4.0.3", 30 | "@rollup/plugin-json": "^4.1.0", 31 | "@rollup/plugin-node-resolve": "^13.0.5", 32 | "aliasify": "^2.1.0", 33 | "browserify": "^17.0.0", 34 | "esbuild": "^0.13.14", 35 | "esbuild-plugin-path-alias": "^1.0.3", 36 | "rollup": "^2.57.0", 37 | "vite": "^2.7.13", 38 | "webpack": "^5.54.0", 39 | "webpack-cli": "^4.8.0" 40 | }, 41 | "dependencies": { 42 | "browser-resolve": "^2.0.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /proxy/process.js: -------------------------------------------------------------------------------- 1 | import { 2 | nextTick, 3 | title, 4 | env as environment, 5 | argv, 6 | version, 7 | versions, 8 | on, 9 | addListener, 10 | once, 11 | off, 12 | removeListener, 13 | removeAllListeners, 14 | emit, 15 | prependListener, 16 | prependOnceListener, 17 | listeners, 18 | cwd, 19 | chdir, 20 | umask, 21 | // @ts-ignore 22 | browser as _browser, 23 | // @ts-ignore 24 | binding as _binding 25 | } from 'process/browser.js'; 26 | 27 | function noop() {} 28 | 29 | const browser = /** @type {boolean} */ (_browser); 30 | const emitWarning = noop; 31 | const binding = /** @type {Function} */ (_binding); 32 | const exit = noop; 33 | const pid = 1; 34 | const features = {}; 35 | const kill = noop; 36 | const dlopen = noop; 37 | const uptime = noop; 38 | const memoryUsage = noop; 39 | const uvCounters = noop; 40 | const platform = 'browser'; 41 | const arch = 'browser'; 42 | const execPath = 'browser'; 43 | const execArgv = /** @type {string[]} */ ([]); 44 | 45 | const api = { 46 | nextTick, 47 | title, 48 | browser, 49 | env: environment, 50 | argv, 51 | version, 52 | versions, 53 | on, 54 | addListener, 55 | once, 56 | off, 57 | removeListener, 58 | removeAllListeners, 59 | emit, 60 | emitWarning, 61 | prependListener, 62 | prependOnceListener, 63 | listeners, 64 | binding, 65 | cwd, 66 | chdir, 67 | umask, 68 | exit, 69 | pid, 70 | features, 71 | kill, 72 | dlopen, 73 | uptime, 74 | memoryUsage, 75 | uvCounters, 76 | platform, 77 | arch, 78 | execPath, 79 | execArgv 80 | }; 81 | 82 | export default api; 83 | 84 | export { 85 | nextTick, 86 | title, 87 | browser, 88 | environment as env, 89 | argv, 90 | version, 91 | versions, 92 | on, 93 | addListener, 94 | once, 95 | off, 96 | removeListener, 97 | removeAllListeners, 98 | emit, 99 | emitWarning, 100 | prependListener, 101 | prependOnceListener, 102 | listeners, 103 | binding, 104 | cwd, 105 | chdir, 106 | umask, 107 | exit, 108 | pid, 109 | features, 110 | kill, 111 | dlopen, 112 | uptime, 113 | memoryUsage, 114 | uvCounters, 115 | platform, 116 | arch, 117 | execPath, 118 | execArgv 119 | }; 120 | -------------------------------------------------------------------------------- /example/esm/index.mjs: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import buffer from 'buffer'; 3 | import child_process from 'child_process'; 4 | import cluster from 'cluster'; 5 | import _console from 'console'; 6 | import constants from 'constants'; 7 | import crypto from 'crypto'; 8 | import dgram from 'dgram'; 9 | import dns from 'dns'; 10 | import domain from 'domain'; 11 | import events from 'events'; 12 | import fs from 'fs'; 13 | import http from 'http'; 14 | import https from 'https'; 15 | import http2 from 'http2'; 16 | import _module from 'module'; 17 | import net from 'net'; 18 | import os from 'os'; 19 | import path from 'path'; 20 | import punycode from 'punycode'; 21 | import _process from 'process'; 22 | import querystring from 'querystring'; 23 | import readline from 'readline'; 24 | import repl from 'repl'; 25 | import stream from 'stream'; 26 | import _stream_duplex from '_stream_duplex'; 27 | import _stream_passthrough from '_stream_passthrough'; 28 | import _stream_readable from '_stream_readable'; 29 | import _stream_transform from '_stream_transform'; 30 | import _stream_writable from '_stream_writable'; 31 | import string_decoder from 'string_decoder'; 32 | import sys from 'sys'; 33 | import timersPromises from 'timers/promises'; 34 | import timers from 'timers'; 35 | import tls from 'tls'; 36 | import tty from 'tty'; 37 | import url from 'url'; 38 | import util from 'util'; 39 | import vm from 'vm'; 40 | import zlib from 'zlib'; 41 | import nodeAssert from 'node:assert'; 42 | 43 | console.log('assert', assert); 44 | console.log('buffer', buffer); 45 | console.log('child_process', child_process); 46 | console.log('cluster', cluster); 47 | console.log('console', _console); 48 | console.log('constants', constants); 49 | console.log('crypto', crypto); 50 | console.log('dgram', dgram); 51 | console.log('dns', dns); 52 | console.log('domain', domain); 53 | console.log('events', events); 54 | console.log('fs', fs); 55 | console.log('http', http); 56 | console.log('https', https); 57 | console.log('http2', http2); 58 | console.log('module', _module); 59 | console.log('net', net); 60 | console.log('os', os); 61 | console.log('path', path); 62 | console.log('punycode', punycode); 63 | console.log('process', _process); 64 | console.log('querystring', querystring); 65 | console.log('readline', readline); 66 | console.log('repl', repl); 67 | console.log('stream', stream); 68 | console.log('_stream_duplex', _stream_duplex); 69 | console.log('_stream_passthrough', _stream_passthrough); 70 | console.log('_stream_readable', _stream_readable); 71 | console.log('_stream_transform', _stream_transform); 72 | console.log('_stream_writable', _stream_writable); 73 | console.log('string_decoder', string_decoder); 74 | console.log('sys', sys); 75 | console.log('timers/promises', timersPromises); 76 | console.log('timers', timers); 77 | console.log('tls', tls); 78 | console.log('tty', tty); 79 | console.log('url', url); 80 | console.log('util', util); 81 | console.log('vm', vm); 82 | console.log('zlib', zlib); 83 | console.log('global Buffer', Buffer); 84 | console.log('global process', process); 85 | console.log('node:assert', nodeAssert); 86 | 87 | export default { 88 | assert, 89 | buffer, 90 | child_process, 91 | cluster, 92 | console: _console, 93 | constants, 94 | crypto, 95 | dgram, 96 | dns, 97 | domain, 98 | events, 99 | fs, 100 | http, 101 | https, 102 | http2, 103 | module: _module, 104 | net, 105 | os, 106 | path, 107 | punycode, 108 | process: _process, 109 | querystring, 110 | readline, 111 | repl, 112 | stream, 113 | _stream_duplex, 114 | _stream_passthrough, 115 | _stream_readable, 116 | _stream_transform, 117 | _stream_writable, 118 | string_decoder, 119 | sys, 120 | 'timers/promises': timersPromises, 121 | timers, 122 | tls, 123 | tty, 124 | url, 125 | util, 126 | vm, 127 | zlib, 128 | __Buffer: Buffer, 129 | __process: process, 130 | __assert: nodeAssert 131 | }; 132 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import createRequire from 'create-require'; 2 | import pkgDir from 'pkg-dir'; 3 | 4 | /** 5 | * @param {string} path 6 | */ 7 | const resolvePath = (path) => { 8 | let resolvedPath; 9 | try { 10 | resolvedPath = require.resolve(path); 11 | } catch { 12 | resolvedPath = ( 13 | globalThis.require ?? createRequire(import.meta.url) 14 | ).resolve(path); 15 | } 16 | if (!path.includes('./')) { 17 | const directory = pkgDir.sync(resolvedPath) ?? ''; 18 | return directory; 19 | } 20 | return resolvedPath; 21 | }; 22 | 23 | const assert = resolvePath('assert/'); 24 | const buffer = resolvePath('buffer/'); 25 | const child_process = resolvePath('./mock/empty.js'); 26 | const cluster = resolvePath('./mock/empty.js'); 27 | const _console = resolvePath('console-browserify'); 28 | const constants = resolvePath('constants-browserify'); 29 | const crypto = resolvePath('crypto-browserify'); 30 | const dgram = resolvePath('./mock/empty.js'); 31 | const dns = resolvePath('./mock/empty.js'); 32 | const domain = resolvePath('domain-browser'); 33 | const events = resolvePath('events/'); 34 | const fs = resolvePath('./mock/empty.js'); 35 | const http = resolvePath('stream-http'); 36 | const https = resolvePath('https-browserify'); 37 | const http2 = resolvePath('./mock/empty.js'); 38 | const _module = resolvePath('./mock/empty.js'); 39 | const net = resolvePath('./mock/empty.js'); 40 | const os = resolvePath('os-browserify/browser.js'); 41 | const path = resolvePath('path-browserify'); 42 | const punycode = resolvePath('punycode/'); 43 | const _process = resolvePath('./proxy/process').replace('.js', ''); 44 | const querystring = resolvePath('./proxy/querystring.js'); 45 | const readline = resolvePath('./mock/empty.js'); 46 | const repl = resolvePath('./mock/empty.js'); 47 | const stream = resolvePath('stream-browserify'); 48 | const _stream_duplex = resolvePath('readable-stream/lib/_stream_duplex.js'); 49 | const _stream_passthrough = resolvePath( 50 | 'readable-stream/lib/_stream_passthrough.js' 51 | ); 52 | const _stream_readable = resolvePath('readable-stream/lib/_stream_readable.js'); 53 | const _stream_transform = resolvePath( 54 | 'readable-stream/lib/_stream_transform.js' 55 | ); 56 | const _stream_writable = resolvePath('readable-stream/lib/_stream_writable.js'); 57 | const string_decoder = resolvePath('string_decoder/'); 58 | const sys = resolvePath('util/util.js'); 59 | const timers = resolvePath('timers-browserify'); 60 | const timersPromises = resolvePath('isomorphic-timers-promises'); 61 | const tls = resolvePath('./mock/empty.js'); 62 | const tty = resolvePath('tty-browserify'); 63 | const url = resolvePath('./proxy/url.js'); 64 | const util = resolvePath('util/util.js'); 65 | const vm = resolvePath('vm-browserify'); 66 | const zlib = resolvePath('browserify-zlib'); 67 | 68 | const packages = { 69 | assert, 70 | buffer, 71 | child_process, 72 | cluster, 73 | console: _console, 74 | constants, 75 | crypto, 76 | dgram, 77 | dns, 78 | domain, 79 | events, 80 | fs, 81 | http, 82 | https, 83 | http2, 84 | module: _module, 85 | net, 86 | os, 87 | path, 88 | punycode, 89 | process: _process, 90 | querystring, 91 | readline, 92 | repl, 93 | stream, 94 | _stream_duplex, 95 | _stream_passthrough, 96 | _stream_readable, 97 | _stream_transform, 98 | _stream_writable, 99 | string_decoder, 100 | sys, 101 | 'timers/promises': timersPromises, 102 | timers, 103 | tls, 104 | tty, 105 | url, 106 | util, 107 | vm, 108 | zlib 109 | }; 110 | 111 | /** @typedef {typeof packages} Packages */ 112 | /** @typedef {keyof Packages} PackageNames */ 113 | /** @typedef {{ [Property in PackageNames as `node:${Property}`]: Packages[Property] }} NodeProtocolPackages */ 114 | 115 | const packagesWithNodeProtocol = /** @type NodeProtocolPackages */ ({}); 116 | for (const [packageName, packagePath] of Object.entries(packages)) { 117 | packagesWithNodeProtocol[ 118 | `node:${/** @type PackageNames */ (packageName)}` 119 | ] = /** @type PackageNames */ packagePath; 120 | } 121 | 122 | export default { 123 | ...packages, 124 | ...packagesWithNodeProtocol 125 | }; 126 | -------------------------------------------------------------------------------- /example/cjs/index.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const buffer = require('buffer'); 3 | const child_process = require('child_process'); 4 | const cluster = require('cluster'); 5 | const _console = require('console'); 6 | const constants = require('constants'); 7 | const crypto = require('crypto'); 8 | const dgram = require('dgram'); 9 | const dns = require('dns'); 10 | const domain = require('domain'); 11 | const events = require('events'); 12 | const fs = require('fs'); 13 | const http = require('http'); 14 | const https = require('https'); 15 | const http2 = require('http2'); 16 | const _module = require('module'); 17 | const net = require('net'); 18 | const os = require('os'); 19 | const path = require('path'); 20 | const punycode = require('punycode'); 21 | const _process = require('process'); 22 | const querystring = require('querystring'); 23 | const readline = require('readline'); 24 | const repl = require('repl'); 25 | const stream = require('stream'); 26 | const _stream_duplex = require('_stream_duplex'); 27 | const _stream_passthrough = require('_stream_passthrough'); 28 | const _stream_readable = require('_stream_readable'); 29 | const _stream_transform = require('_stream_transform'); 30 | const _stream_writable = require('_stream_writable'); 31 | const string_decoder = require('string_decoder'); 32 | const sys = require('sys'); 33 | const timersPromises = require('timers/promises'); 34 | const timers = require('timers'); 35 | const tls = require('tls'); 36 | const tty = require('tty'); 37 | const url = require('url'); 38 | const util = require('util'); 39 | const vm = require('vm'); 40 | const zlib = require('zlib'); 41 | const nodeAssert = require('node:assert'); 42 | 43 | console.log('assert', assert); 44 | console.log('buffer', buffer); 45 | console.log('child_process', child_process); 46 | console.log('cluster', cluster); 47 | console.log('console', _console); 48 | console.log('constants', constants); 49 | console.log('crypto', crypto); 50 | console.log('dgram', dgram); 51 | console.log('dns', dns); 52 | console.log('domain', domain); 53 | console.log('events', events); 54 | console.log('fs', fs); 55 | console.log('http', http); 56 | console.log('https', https); 57 | console.log('http2', http2); 58 | console.log('module', _module); 59 | console.log('net', net); 60 | console.log('os', os); 61 | console.log('path', path); 62 | console.log('punycode', punycode); 63 | console.log('process', _process); 64 | console.log('querystring', querystring); 65 | console.log('readline', readline); 66 | console.log('repl', repl); 67 | console.log('stream', stream); 68 | console.log('_stream_duplex', _stream_duplex); 69 | console.log('_stream_passthrough', _stream_passthrough); 70 | console.log('_stream_readable', _stream_readable); 71 | console.log('_stream_transform', _stream_transform); 72 | console.log('_stream_writable', _stream_writable); 73 | console.log('string_decoder', string_decoder); 74 | console.log('sys', sys); 75 | console.log('timers/promises', timersPromises); 76 | console.log('timers', timers); 77 | console.log('tls', tls); 78 | console.log('tty', tty); 79 | console.log('url', url); 80 | console.log('util', util); 81 | console.log('vm', vm); 82 | console.log('zlib', zlib); 83 | console.log('global Buffer', Buffer); 84 | console.log('global process', process); 85 | console.log('node:assert', nodeAssert); 86 | 87 | module.exports = { 88 | assert, 89 | buffer, 90 | child_process, 91 | cluster, 92 | console: _console, 93 | constants, 94 | crypto, 95 | dgram, 96 | dns, 97 | domain, 98 | events, 99 | fs, 100 | http, 101 | https, 102 | http2, 103 | module: _module, 104 | net, 105 | os, 106 | path, 107 | punycode, 108 | process: _process, 109 | querystring, 110 | readline, 111 | repl, 112 | stream, 113 | _stream_duplex, 114 | _stream_passthrough, 115 | _stream_readable, 116 | _stream_transform, 117 | _stream_writable, 118 | string_decoder, 119 | sys, 120 | 'timers/promises': timersPromises, 121 | timers, 122 | tls, 123 | tty, 124 | url, 125 | util, 126 | vm, 127 | zlib, 128 | __Buffer: Buffer, 129 | __process: process, 130 | __assert: nodeAssert 131 | }; 132 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const { promises: fs } = require('fs'); 5 | const { default: babel } = require('@rollup/plugin-babel'); 6 | const commonjs = require('@rollup/plugin-commonjs'); 7 | const execa = require('execa'); 8 | const cpy = require('cpy'); 9 | const del = require('del'); 10 | 11 | function getConfig(filename, options = {}) { 12 | const { cjsOutro = '', cjsExports = 'auto' } = options; 13 | return { 14 | input: filename, 15 | output: [ 16 | { 17 | file: `cjs/${filename}`, 18 | format: 'cjs', 19 | exports: cjsExports, 20 | sourcemap: true, 21 | outro: cjsOutro 22 | }, 23 | { 24 | file: `esm/${filename}`, 25 | format: 'esm', 26 | sourcemap: true 27 | } 28 | ], 29 | plugins: [ 30 | (() => { 31 | return { 32 | name: 'resolve-id', 33 | async resolveId(source, importer) { 34 | if (source === 'url') { 35 | return require.resolve('url/'); 36 | } 37 | if (source === 'process/browser.js') { 38 | return require.resolve('process/browser.js'); 39 | } 40 | if ( 41 | source === 'path' && 42 | importer.includes('proxy/url.js') 43 | ) { 44 | return require.resolve( 45 | 'rollup-plugin-node-builtins/src/es6/path.js' 46 | ); 47 | } 48 | return null; 49 | } 50 | }; 51 | })(), 52 | (() => { 53 | return { 54 | name: 'types', 55 | async writeBundle(output) { 56 | let prefix; 57 | if (output.file.includes('cjs/')) { 58 | prefix = 'cjs'; 59 | } else if (output.file.includes('esm/')) { 60 | prefix = 'esm'; 61 | } 62 | if (typeof prefix !== 'undefined') { 63 | const tsconfig = { 64 | extends: './tsconfig', 65 | exclude: [ 66 | 'test/**/*.js', 67 | 'types/**/*-test*', 68 | 'helpers/**/*.js' 69 | ], 70 | compilerOptions: { 71 | declaration: true, 72 | declarationMap: true, 73 | declarationDir: prefix, 74 | emitDeclarationOnly: true, 75 | noEmit: false, 76 | listEmittedFiles: true 77 | } 78 | }; 79 | const file = `.${prefix}.tsconfig.json`; 80 | try { 81 | try { 82 | await cpy('types/**/*.d.ts', 'types', { 83 | rename: (basename) => 84 | basename.replace('.d.ts', '.ts') 85 | }); 86 | } catch (error) {} 87 | await fs.writeFile( 88 | file, 89 | JSON.stringify(tsconfig), 90 | 'utf-8' 91 | ); 92 | const { stdout } = await execa( 93 | 'tsc', 94 | ['-p', file], 95 | { 96 | preferLocal: true 97 | } 98 | ); 99 | try { 100 | await del([ 101 | 'types/**/*.ts', 102 | '!types/**/*.d.ts' 103 | ]); 104 | } catch (error) {} 105 | console.log(stdout); 106 | } finally { 107 | await fs.unlink(file); 108 | } 109 | } 110 | } 111 | }; 112 | })(), 113 | (() => { 114 | return { 115 | name: 'package-type', 116 | async writeBundle(output) { 117 | let prefix, type; 118 | if (output.file.includes('index.js')) { 119 | return; 120 | } 121 | if (output.file.includes('cjs/')) { 122 | prefix = 'cjs'; 123 | type = 'commonjs'; 124 | } else if (output.file.includes('esm/')) { 125 | prefix = 'esm'; 126 | type = 'module'; 127 | } 128 | if (typeof prefix !== 'undefined') { 129 | const package_ = path.join(prefix, 'package.json'); 130 | try { 131 | await fs.unlink(package_); 132 | } catch (error) {} 133 | await fs.writeFile( 134 | package_, 135 | JSON.stringify({ type }), 136 | 'utf8' 137 | ); 138 | } 139 | } 140 | }; 141 | })(), 142 | commonjs(), 143 | babel({ 144 | babelHelpers: 'bundled', 145 | exclude: 'node_modules/**' 146 | }) 147 | ] 148 | }; 149 | } 150 | 151 | module.exports = [ 152 | 'index.js', 153 | 'mock/buffer.js', 154 | 'mock/console.js', 155 | 'mock/dns.js', 156 | 'mock/empty.js', 157 | 'mock/net.js', 158 | 'mock/process.js', 159 | 'mock/punycode.js', 160 | 'mock/tls.js', 161 | 'mock/tty.js', 162 | [ 163 | 'proxy/url.js', 164 | { cjsOutro: 'exports = module.exports = api;', cjsExports: 'named' } 165 | ], 166 | [ 167 | 'proxy/querystring.js', 168 | { cjsOutro: 'exports = module.exports = api;', cjsExports: 'named' } 169 | ], 170 | [ 171 | 'proxy/process.js', 172 | { cjsOutro: 'exports = module.exports = api;', cjsExports: 'named' } 173 | ], 174 | [ 175 | 'proxy/process/browser.js', 176 | { cjsOutro: 'exports = module.exports = api;', cjsExports: 'named' } 177 | ] 178 | ].map((entry) => { 179 | const [filename, options = {}] = [].concat(entry); 180 | return getConfig(filename, options); 181 | }); 182 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-stdlib-browser", 3 | "version": "1.3.1", 4 | "description": "Node standard library for browser.", 5 | "license": "MIT", 6 | "author": "Ivan Nikolić (http://ivannikolic.com)", 7 | "sideEffects": false, 8 | "exports": { 9 | ".": { 10 | "import": "./esm/index.js", 11 | "require": "./cjs/index.js" 12 | }, 13 | "./mock/*": { 14 | "import": "./esm/mock/*.js", 15 | "require": "./cjs/mock/*.js" 16 | }, 17 | "./helpers/esbuild/*": "./helpers/esbuild/*.js", 18 | "./helpers/webpack/*": "./helpers/webpack/*.js", 19 | "./helpers/rollup/*": "./helpers/rollup/*.js", 20 | "./package.json": "./package.json" 21 | }, 22 | "main": "cjs/index.js", 23 | "module": "esm/index.js", 24 | "types": "esm/index.d.ts", 25 | "directories": { 26 | "test": "test" 27 | }, 28 | "files": [ 29 | "cjs/", 30 | "esm/", 31 | "helpers/esbuild/{plugin,shim}.js", 32 | "helpers/webpack/plugin.js", 33 | "helpers/rollup/plugin.js", 34 | "CHANGELOG.md", 35 | "LICENSE.md", 36 | "README.md" 37 | ], 38 | "scripts": { 39 | "build": "del '{esm/,cjs/}' && rollup --config rollup.config.js && babel helpers/esbuild/shim.src.js --out-file=helpers/esbuild/shim.js", 40 | "lint": "eslint '{index,lib/**/*,test/**/*,helpers/**/*,example/**/*}.{js,mjs}'", 41 | "lint:types": "tsc", 42 | "module-check": "node -e 'require(\"node-stdlib-browser\"); require(\"node-stdlib-browser/helpers/esbuild/plugin\");' && node --input-type=module -e 'import \"node-stdlib-browser\"; import \"node-stdlib-browser/helpers/esbuild/plugin\";'", 43 | "prepublishOnly": "npm run build", 44 | "postpublish": "GITHUB_TOKEN=$GITHUB_RELEASE_TOKEN github-release-from-changelog", 45 | "prerelease": "npm run lint && npm run lint:types && npm run build && npm run module-check", 46 | "release": "np --no-release-draft", 47 | "test": "BABEL_ENV=test nyc mocha --require @babel/register --require esm 'test/**/*.js' && nyc check-coverage", 48 | "test:watch": "nodemon --exec npm test", 49 | "version": "if [ $(git rev-parse --abbrev-ref HEAD) == 'master' ]; then sed -i '' '/\\[unreleased\\]:/d' CHANGELOG.md && version-changelog CHANGELOG.md && changelog-verify CHANGELOG.md && git add CHANGELOG.md; else echo; fi" 50 | }, 51 | "dependencies": { 52 | "assert": "^2.0.0", 53 | "browser-resolve": "^2.0.0", 54 | "browserify-zlib": "^0.2.0", 55 | "buffer": "^5.7.1", 56 | "console-browserify": "^1.1.0", 57 | "constants-browserify": "^1.0.0", 58 | "create-require": "^1.1.1", 59 | "crypto-browserify": "^3.12.1", 60 | "domain-browser": "4.22.0", 61 | "events": "^3.0.0", 62 | "https-browserify": "^1.0.0", 63 | "isomorphic-timers-promises": "^1.0.1", 64 | "os-browserify": "^0.3.0", 65 | "path-browserify": "^1.0.1", 66 | "pkg-dir": "^5.0.0", 67 | "process": "^0.11.10", 68 | "punycode": "^1.4.1", 69 | "querystring-es3": "^0.2.1", 70 | "readable-stream": "^3.6.0", 71 | "stream-browserify": "^3.0.0", 72 | "stream-http": "^3.2.0", 73 | "string_decoder": "^1.0.0", 74 | "timers-browserify": "^2.0.4", 75 | "tty-browserify": "0.0.1", 76 | "url": "^0.11.4", 77 | "util": "^0.12.4", 78 | "vm-browserify": "^1.0.1" 79 | }, 80 | "devDependencies": { 81 | "@babel/cli": "^7.2.3", 82 | "@babel/core": "^7.2.2", 83 | "@babel/preset-env": "^7.12.1", 84 | "@babel/register": "^7.0.0", 85 | "@rollup/plugin-babel": "^5.2.1", 86 | "@rollup/plugin-commonjs": "^20.0.0", 87 | "@types/browser-resolve": "^2.0.1", 88 | "@types/mocha": "^8.2.3", 89 | "@types/node": "^16.3.0", 90 | "@types/parse-node-version": "^1.0.0", 91 | "babel-plugin-istanbul": "^6.0.0", 92 | "babel-plugin-transform-globalthis": "^1.0.0", 93 | "changelog-verify": "^1.1.2", 94 | "core-js": "^2.6.5", 95 | "cpy": "^8.1.2", 96 | "del": "^6.0.0", 97 | "del-cli": "^3.0.1", 98 | "esbuild": "^0.13.14", 99 | "eslint": "^7.31.0", 100 | "eslint-config-niksy": "^10.0.0", 101 | "eslint-config-prettier": "^8.3.0", 102 | "eslint-plugin-import": "^2.23.4", 103 | "eslint-plugin-jsdoc": "^33.3.0", 104 | "eslint-plugin-mocha": "^8.0.0", 105 | "eslint-plugin-node": "^11.1.0", 106 | "eslint-plugin-prettier": "^3.4.0", 107 | "eslint-plugin-promise": "^5.1.0", 108 | "eslint-plugin-unicorn": "^31.0.0", 109 | "esm": "^3.0.51", 110 | "execa": "^5.1.1", 111 | "github-release-from-changelog": "^2.1.1", 112 | "husky": "^4.3.0", 113 | "lint-staged": "^10.4.2", 114 | "mocha": "^8.2.0", 115 | "nodemon": "^2.0.6", 116 | "np": "^7.6.0", 117 | "nyc": "^15.1.0", 118 | "parse-node-version": "^1.0.1", 119 | "prettier": "^2.4.0", 120 | "rollup": "^2.32.1", 121 | "rollup-plugin-node-builtins": "^2.1.2", 122 | "typescript": "^4.3.5", 123 | "version-changelog": "^3.1.1", 124 | "webpack": "^5.65.0" 125 | }, 126 | "engines": { 127 | "node": ">=10" 128 | }, 129 | "keywords": [ 130 | "node", 131 | "std", 132 | "browser", 133 | "api" 134 | ], 135 | "repository": { 136 | "type": "git", 137 | "url": "git+https://github.com/niksy/node-stdlib-browser.git" 138 | }, 139 | "bugs": { 140 | "url": "https://github.com/niksy/node-stdlib-browser/issues" 141 | }, 142 | "homepage": "https://github.com/niksy/node-stdlib-browser#readme" 143 | } 144 | -------------------------------------------------------------------------------- /proxy/url.js: -------------------------------------------------------------------------------- 1 | /* globals unknown */ 2 | 3 | /** 4 | * @typedef {import('url').URLFormatOptions} URLFormatOptions 5 | * @typedef {import('url').UrlObject} UrlObject 6 | * @typedef {import('url').format} formatImport 7 | * @typedef {import('url').parse} parseImport 8 | * @typedef {import('url').resolve} resolveImport 9 | * @typedef {import('url').Url} UrlImport 10 | * @typedef {import('url').fileURLToPath} fileURLToPath 11 | * @typedef {import('url').pathToFileURL} pathToFileURL 12 | * @typedef {import('url').domainToUnicode} domainToUnicode 13 | * @typedef {import('url').domainToASCII} domainToASCII 14 | */ 15 | 16 | // @ts-ignore 17 | import { format, parse, resolve, resolveObject, Url } from 'url'; 18 | import { resolve as pathResolve } from 'path'; 19 | 20 | const formatImport = /** @type {formatImport}*/ (format); 21 | const parseImport = /** @type {parseImport}*/ (parse); 22 | const resolveImport = /** @type {resolveImport}*/ (resolve); 23 | // @ts-ignore 24 | const UrlImport = /** @type {UrlImport}*/ (Url); 25 | 26 | const URL = globalThis.URL; 27 | /* eslint-disable-next-line unicorn/prevent-abbreviations */ 28 | const URLSearchParams = globalThis.URLSearchParams; 29 | 30 | const percentRegEx = /%/g; 31 | const backslashRegEx = /\\/g; 32 | const newlineRegEx = /\n/g; 33 | const carriageReturnRegEx = /\r/g; 34 | const tabRegEx = /\t/g; 35 | const CHAR_FORWARD_SLASH = 47; 36 | 37 | /** 38 | * @param {unknown} instance 39 | */ 40 | function isURLInstance(instance) { 41 | const resolved = /** @type {URL|null} */ (instance ?? null); 42 | return Boolean(resolved !== null && resolved?.href && resolved?.origin); 43 | } 44 | 45 | /** 46 | * @param {URL} url 47 | */ 48 | function getPathFromURLPosix(url) { 49 | if (url.hostname !== '') { 50 | throw new TypeError( 51 | `File URL host must be "localhost" or empty on browser` 52 | ); 53 | } 54 | const pathname = url.pathname; 55 | for (let n = 0; n < pathname.length; n++) { 56 | if (pathname[n] === '%') { 57 | // @ts-ignore 58 | const third = pathname.codePointAt(n + 2) | 0x20; 59 | if (pathname[n + 1] === '2' && third === 102) { 60 | throw new TypeError( 61 | 'File URL path must not include encoded / characters' 62 | ); 63 | } 64 | } 65 | } 66 | return decodeURIComponent(pathname); 67 | } 68 | 69 | /** 70 | * @param {string} filepath 71 | */ 72 | function encodePathChars(filepath) { 73 | if (filepath.includes('%')) { 74 | filepath = filepath.replace(percentRegEx, '%25'); 75 | } 76 | if (filepath.includes('\\')) { 77 | filepath = filepath.replace(backslashRegEx, '%5C'); 78 | } 79 | if (filepath.includes('\n')) { 80 | filepath = filepath.replace(newlineRegEx, '%0A'); 81 | } 82 | if (filepath.includes('\r')) { 83 | filepath = filepath.replace(carriageReturnRegEx, '%0D'); 84 | } 85 | if (filepath.includes('\t')) { 86 | filepath = filepath.replace(tabRegEx, '%09'); 87 | } 88 | return filepath; 89 | } 90 | 91 | const domainToASCII = 92 | /** 93 | * @type {domainToASCII} 94 | */ 95 | function (domain) { 96 | if (typeof domain === 'undefined') { 97 | throw new TypeError('The "domain" argument must be specified'); 98 | } 99 | return new URL(`http://${domain}`).hostname; 100 | }; 101 | 102 | const domainToUnicode = 103 | /** 104 | * @type {domainToUnicode} 105 | */ 106 | function (domain) { 107 | if (typeof domain === 'undefined') { 108 | throw new TypeError('The "domain" argument must be specified'); 109 | } 110 | return new URL(`http://${domain}`).hostname; 111 | }; 112 | 113 | const pathToFileURL = 114 | /** 115 | * @type {(url: string) => URL} 116 | */ 117 | function (filepath) { 118 | const outURL = new URL('file://'); 119 | let resolved = pathResolve(filepath); 120 | const filePathLast = filepath.charCodeAt(filepath.length - 1); 121 | if ( 122 | filePathLast === CHAR_FORWARD_SLASH && 123 | resolved[resolved.length - 1] !== '/' 124 | ) { 125 | resolved += '/'; 126 | } 127 | outURL.pathname = encodePathChars(resolved); 128 | return outURL; 129 | }; 130 | 131 | const fileURLToPath = 132 | /** 133 | * @type {fileURLToPath & ((path: string | URL) => string)} 134 | */ 135 | function (path) { 136 | if (!isURLInstance(path) && typeof path !== 'string') { 137 | throw new TypeError( 138 | `The "path" argument must be of type string or an instance of URL. Received type ${typeof path} (${path})` 139 | ); 140 | } 141 | const resolved = new URL(path); 142 | if (resolved.protocol !== 'file:') { 143 | throw new TypeError('The URL must be of scheme file'); 144 | } 145 | return getPathFromURLPosix(resolved); 146 | }; 147 | 148 | const formatImportWithOverloads = 149 | /** 150 | * @type {( 151 | * ((urlObject: URL, options?: URLFormatOptions) => string) & 152 | * ((urlObject: UrlObject | string, options?: never) => string) 153 | * )} 154 | */ 155 | function (urlObject, options = {}) { 156 | if (!(urlObject instanceof URL)) { 157 | return formatImport(urlObject); 158 | } 159 | 160 | if (typeof options !== 'object' || options === null) { 161 | throw new TypeError( 162 | 'The "options" argument must be of type object.' 163 | ); 164 | } 165 | 166 | const auth = options.auth ?? true; 167 | const fragment = options.fragment ?? true; 168 | const search = options.search ?? true; 169 | const unicode = options.unicode ?? false; 170 | 171 | const parsed = new URL(urlObject.toString()); 172 | 173 | if (!auth) { 174 | parsed.username = ''; 175 | parsed.password = ''; 176 | } 177 | 178 | if (!fragment) { 179 | parsed.hash = ''; 180 | } 181 | 182 | if (!search) { 183 | parsed.search = ''; 184 | } 185 | 186 | if (unicode) { 187 | // Not implemented 188 | } 189 | 190 | return parsed.toString(); 191 | }; 192 | 193 | const api = { 194 | format: formatImportWithOverloads, 195 | parse: parseImport, 196 | resolve: resolveImport, 197 | resolveObject, 198 | Url: UrlImport, 199 | URL, 200 | URLSearchParams, 201 | domainToASCII, 202 | domainToUnicode, 203 | pathToFileURL, 204 | fileURLToPath 205 | }; 206 | 207 | export default api; 208 | 209 | export { 210 | formatImportWithOverloads as format, 211 | parseImport as parse, 212 | resolveImport as resolve, 213 | resolveObject, 214 | UrlImport as Url, 215 | URL, 216 | URLSearchParams, 217 | domainToASCII, 218 | domainToUnicode, 219 | pathToFileURL, 220 | fileURLToPath 221 | }; 222 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-stdlib-browser 2 | 3 | [![Build Status][ci-img]][ci] 4 | 5 | [Node standard library](https://nodejs.org/docs/latest/api/) for browser. 6 | 7 | Features: 8 | 9 | - Based on [`node-libs-browser`](https://github.com/webpack/node-libs-browser) 10 | for Webpack 11 | - Maintained with newer versions and modern implementations 12 | - Works with Webpack, Rollup, Vite, esbuild and Browserify, but should also 13 | work with other bundlers 14 | - Exports implementation with [`node:` protocol][node-protocol-imports] which 15 | allows for builtin modules to be referenced by valid absolute URL strings 16 | 17 | Check [example](/example) to see how modules work in browser environment. 18 | 19 | ## Install 20 | 21 | ```sh 22 | npm install node-stdlib-browser --save-dev 23 | ``` 24 | 25 | ## Usage 26 | 27 | ### Webpack 28 | 29 |
30 | 31 | Show me 32 | 33 | As of Webpack 5, aliases and globals provider need to be explicitly configured. 34 | If you want to handle [`node:` protocol][node-protocol-imports] imports, you 35 | need to provide helper plugin. 36 | 37 | ```js 38 | // webpack.config.js 39 | const stdLibBrowser = require('node-stdlib-browser'); 40 | const { 41 | NodeProtocolUrlPlugin 42 | } = require('node-stdlib-browser/helpers/webpack/plugin'); 43 | const webpack = require('webpack'); 44 | 45 | module.exports = { 46 | // ... 47 | resolve: { 48 | alias: stdLibBrowser 49 | }, 50 | plugins: [ 51 | new NodeProtocolUrlPlugin(), 52 | new webpack.ProvidePlugin({ 53 | process: stdLibBrowser.process, 54 | Buffer: [stdLibBrowser.buffer, 'Buffer'] 55 | }) 56 | ] 57 | }; 58 | ``` 59 | 60 | If you’re using ESM config, additional configuration is needed to handle 61 | unspecified extensions: 62 | 63 | ```js 64 | // webpack.config.js 65 | module.exports = { 66 | // ... 67 | module: { 68 | rules: [ 69 | { 70 | test: /\.m?js$/, 71 | resolve: { 72 | fullySpecified: false 73 | } 74 | } 75 | ] 76 | } 77 | }; 78 | ``` 79 | 80 |
81 | 82 | ### Rollup 83 | 84 |
85 | 86 | Show me 87 | 88 | Since many packages expose only CommonJS implementation, you need to apply 89 | plugins to handle CommonJS exports. Those packages could have dependencies 90 | installed with npm so they need to be properly resolved (taking into account 91 | browser-specific implementations). 92 | 93 | Some dependencies can have circular dependencies and Rollup will warn you about 94 | that. You can ignore these warnings with helper function 95 | ([reference](<(https://github.com/rollup/rollup/issues/1089#issuecomment-635564942)>)). 96 | 97 | ```js 98 | // rollup.config.js 99 | const stdLibBrowser = require('node-stdlib-browser'); 100 | const { 101 | handleCircularDependancyWarning 102 | } = require('node-stdlib-browser/helpers/rollup/plugin'); 103 | const { default: resolve } = require('@rollup/plugin-node-resolve'); 104 | const commonjs = require('@rollup/plugin-commonjs'); 105 | const json = require('@rollup/plugin-json'); 106 | const alias = require('@rollup/plugin-alias'); 107 | const inject = require('@rollup/plugin-inject'); 108 | 109 | module.exports = { 110 | // ... 111 | plugins: [ 112 | alias({ 113 | entries: stdLibBrowser 114 | }), 115 | resolve({ 116 | browser: true 117 | }), 118 | commonjs(), 119 | json(), 120 | inject({ 121 | process: stdLibBrowser.process, 122 | Buffer: [stdLibBrowser.buffer, 'Buffer'] 123 | }) 124 | ], 125 | onwarn: (warning, rollupWarn) => { 126 | handleCircularDependancyWarning(warning, rollupWarn); 127 | } 128 | }; 129 | ``` 130 | 131 |
132 | 133 | ### Vite 134 | 135 |
136 | 137 | Show me 138 | 139 | Vite config uses combination of Rollup and esbuild plugins. It’s **important** 140 | to use dynamic import when using CommonJS configuration so ESM version of 141 | modules is picked up. This allows Vite bundling to use our mocking 142 | implementation and implement heuristics such as proper tree-shaking and dead 143 | code removal marking. 144 | 145 | ```js 146 | const inject = require('@rollup/plugin-inject'); 147 | 148 | const esbuildShim = require.resolve('node-stdlib-browser/helpers/esbuild/shim'); 149 | 150 | module.exports = async () => { 151 | const { default: stdLibBrowser } = await import('node-stdlib-browser'); 152 | return { 153 | resolve: { 154 | alias: stdLibBrowser 155 | }, 156 | optimizeDeps: { 157 | include: ['buffer', 'process'] 158 | }, 159 | plugins: [ 160 | { 161 | ...inject({ 162 | global: [esbuildShim, 'global'], 163 | process: [esbuildShim, 'process'], 164 | Buffer: [esbuildShim, 'Buffer'] 165 | }), 166 | enforce: 'post' 167 | } 168 | ] 169 | }; 170 | }; 171 | ``` 172 | 173 | #### Vite plugins 174 | 175 | If you wish to use simpler configuration, you can use one of the available Vite 176 | plugins which use this package under the hood: 177 | 178 | - https://github.com/sodatea/vite-plugin-node-stdlib-browser 179 | - https://github.com/davidmyersdev/vite-plugin-node-polyfills 180 | 181 |
182 | 183 | ### esbuild 184 | 185 |
186 | 187 | Show me 188 | 189 | Using esbuild requires you to use helper utilities and plugins. 190 | 191 | ```js 192 | const path = require('path'); 193 | const esbuild = require('esbuild'); 194 | const plugin = require('node-stdlib-browser/helpers/esbuild/plugin'); 195 | const stdLibBrowser = require('node-stdlib-browser'); 196 | 197 | (async () => { 198 | await esbuild.build({ 199 | // ... 200 | inject: [require.resolve('node-stdlib-browser/helpers/esbuild/shim')], 201 | define: { 202 | global: 'global', 203 | process: 'process', 204 | Buffer: 'Buffer' 205 | }, 206 | plugins: [plugin(stdLibBrowser)] 207 | }); 208 | })(); 209 | ``` 210 | 211 |
212 | 213 | ### Browserify 214 | 215 |
216 | 217 | Show me 218 | 219 | Bundling ES modules is currently not supported natively in Browserify, but you 220 | can try using [esmify](https://github.com/mattdesl/esmify) or 221 | [babelify](https://github.com/babel/babelify) for transforming to CommonJS 222 | first. 223 | 224 | ```js 225 | const fs = require('fs'); 226 | const path = require('path'); 227 | const browserify = require('browserify'); 228 | const aliasify = require('aliasify'); 229 | const stdLibBrowser = require('node-stdlib-browser'); 230 | 231 | const b = browserify( 232 | [ 233 | /* ... */ 234 | ], 235 | { 236 | // ... 237 | transform: [[aliasify, { aliases: stdLibBrowser }]], 238 | insertGlobalVars: { 239 | process: () => { 240 | return `require('${stdLibBrowser.process}')`; 241 | }, 242 | Buffer: () => { 243 | return `require('${stdLibBrowser.buffer}').Buffer`; 244 | } 245 | } 246 | } 247 | ); 248 | ``` 249 | 250 |
251 | 252 | ## Package contents 253 | 254 | | Module | Browser implementation | Mock implementation | Notes | 255 | | --------------------- | --------------------------------------------------------------------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------- | 256 | | `assert` | [assert](https://github.com/browserify/commonjs-assert) | | 257 | | `buffer` | [buffer](https://github.com/feross/buffer) | [buffer](mock/buffer.js) | `buffer@5` for IE 11 support | 258 | | `child_process` | | | 259 | | `cluster` | | | 260 | | `console` | [console-browserify](https://github.com/browserify/console-browserify) | [console](mock/console.js) | 261 | | `constants` | [constants-browserify](https://github.com/juliangruber/constants-browserify) | | 262 | | `crypto` | [crypto-browserify](https://github.com/crypto-browserify/crypto-browserify) | | 263 | | `dgram` | | | 264 | | `dns` | | [dns](mock/dns.js) | 265 | | `domain` | [domain-browser](https://github.com/bevry/domain-browser) | | 266 | | `events` | [events](https://github.com/browserify/events) | | 267 | | `fs` | | | [Mocking `fs`](#mocking-fs) | 268 | | `http` | [stream-http](https://github.com/jhiesey/stream-http) | | 269 | | `https` | [https-browserify](https://github.com/substack/https-browserify) | | 270 | | `module` | | | 271 | | `net` | | [net](mock/net.js) | 272 | | `os` | [os-browserify](https://github.com/CoderPuppy/os-browserify) | | 273 | | `path` | [path-browserify](https://github.com/browserify/path-browserify) | | 274 | | `process` | [process](https://github.com/defunctzombie/node-process) | [process](mock/process.js) | Contains additional exports from newer Node | 275 | | `punycode` | [punycode](https://github.com/bestiejs/punycode.js) | | `punycode@1` for browser support | 276 | | `querystring` | [querystring-es3](https://github.com/mike-spainhower/querystring) | | Contains additional exports from newer Node versions | 277 | | `readline` | | | 278 | | `repl` | | | 279 | | `stream` | [stream-browserify](https://github.com/browserify/stream-browserify) | | 280 | | `string_decoder` | [string_decoder](https://github.com/nodejs/string_decoder) | | 281 | | `sys` | [util](https://github.com/browserify/node-util) | | 282 | | `timers` | [timers-browserify](https://github.com/browserify/timers-browserify) | | 283 | | `timers/promises` | [isomorphic-timers-promises](https://github.com/niksy/isomorphic-timers-promises) | | 284 | | `tls` | | [tls](mock/tls.js) | 285 | | `tty` | [tty-browserify](https://github.com/browserify/tty-browserify) | [tty](mock/tty.js) | 286 | | `url` | [node-url](https://github.com/defunctzombie/node-url) | | Contains additional exports from newer Node versions (`URL` and `URLSearchParams` are not polyfilled) | 287 | | `util` | [util](https://github.com/browserify/node-util) | | 288 | | `vm` | [vm-browserify](https://github.com/browserify/vm-browserify) | | 289 | | `zlib` | [browserify-zlib](https://github.com/browserify/browserify-zlib) | | 290 | | `_stream_duplex` | [readable-stream](https://github.com/nodejs/readable-stream) | | 291 | | `_stream_passthrough` | [readable-stream](https://github.com/nodejs/readable-stream) | | 292 | | `_stream_readable` | [readable-stream](https://github.com/nodejs/readable-stream) | | 293 | | `_stream_transform` | [readable-stream](https://github.com/nodejs/readable-stream) | | 294 | | `_stream_writable` | [readable-stream](https://github.com/nodejs/readable-stream) | | 295 | 296 | ## API 297 | 298 | ### packages 299 | 300 | Returns: `object` 301 | 302 | Exports absolute paths to each module directory (where `package.json` is 303 | located), keyed by module names. Modules without browser replacements return 304 | module with default export `null`. 305 | 306 | Some modules have mocks in the mock directory. These are replacements with 307 | minimal functionality. 308 | 309 | ## Tips 310 | 311 | ### Mocking `fs` 312 | 313 | `fs` package doesn’t return anything since there are many different ways you can 314 | implement file system functionality in browser. 315 | 316 | Examples of implementations: 317 | 318 | - [`BrowserFS`](https://github.com/jvilk/BrowserFS) 319 | - [`fs-web`](https://github.com/matthewp/fs) 320 | - [`browserify-fs`](https://github.com/mafintosh/browserify-fs) 321 | - [`mock-fs`](https://github.com/tschaub/mock-fs) 322 | - [`memfs`](https://github.com/streamich/memfs) 323 | 324 | ## Node support 325 | 326 | Minimum supported version should be Node 10. 327 | 328 | If you’re using ESM in Node < 12.20, note that 329 | [subpath patterns](https://nodejs.org/api/packages.html#packages_subpath_patterns) 330 | are not supported so mocks can’t be handled. In that case, it’s recommended to 331 | use CommonJS implementation. 332 | 333 | ## Browser support 334 | 335 | Minimum supported version should be Internet Explorer 11, but most modules 336 | support even Internet Explorer 9. 337 | 338 | ## Types 339 | 340 | You can use default `@types/node` types. 341 | 342 | ## License 343 | 344 | MIT © [Ivan Nikolić](http://ivannikolic.com) 345 | 346 | 347 | 348 | [ci]: https://github.com/niksy/node-stdlib-browser/actions?query=workflow%3ACI 349 | [ci-img]: https://github.com/niksy/node-stdlib-browser/workflows/CI/badge.svg?branch=master 350 | [node-protocol-imports]: https://nodejs.org/api/esm.html#node-imports 351 | 352 | 353 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-namespace */ 2 | 3 | import assert from 'assert'; 4 | import path from 'path'; 5 | import execa from 'execa'; 6 | import parseNodeVersion from 'parse-node-version'; 7 | import api from '../index'; 8 | import url from '../proxy/url'; 9 | import qs from '../proxy/querystring'; 10 | import _process from '../proxy/process'; 11 | 12 | /** @typedef {import('../index').PackageNames} PackageNames */ 13 | 14 | const context = path.resolve(__dirname, '../'); 15 | 16 | const packages = { 17 | _stream_duplex: 'node_modules/readable-stream', 18 | _stream_passthrough: 'node_modules/readable-stream', 19 | _stream_readable: 'node_modules/readable-stream', 20 | _stream_transform: 'node_modules/readable-stream', 21 | _stream_writable: 'node_modules/readable-stream', 22 | assert: 'node_modules/assert', 23 | buffer: 'node_modules/buffer', 24 | child_process: 'mock/empty.js', 25 | cluster: 'mock/empty.js', 26 | console: 'node_modules/console-browserify', 27 | constants: 'node_modules/constants-browserify', 28 | crypto: 'node_modules/crypto-browserify', 29 | dgram: 'mock/empty.js', 30 | dns: 'mock/empty.js', 31 | domain: 'node_modules/domain-browser', 32 | events: 'node_modules/events', 33 | fs: 'mock/empty.js', 34 | http: 'node_modules/stream-http', 35 | https: 'node_modules/https-browserify', 36 | http2: 'mock/empty.js', 37 | module: 'mock/empty.js', 38 | net: 'mock/empty.js', 39 | os: 'node_modules/os-browserify', 40 | path: 'node_modules/path-browserify', 41 | process: 'proxy/process', 42 | punycode: 'node_modules/punycode', 43 | querystring: 'proxy/querystring.js', 44 | readline: 'mock/empty.js', 45 | repl: 'mock/empty.js', 46 | stream: 'node_modules/stream-browserify', 47 | string_decoder: 'node_modules/string_decoder', 48 | sys: 'node_modules/util', 49 | timers: 'node_modules/timers-browserify', 50 | 'timers/promises': 'node_modules/isomorphic-timers-promises/cjs', 51 | tls: 'mock/empty.js', 52 | tty: 'node_modules/tty-browserify', 53 | url: 'proxy/url.js', 54 | util: 'node_modules/util', 55 | vm: 'node_modules/vm-browserify', 56 | zlib: 'node_modules/browserify-zlib' 57 | }; 58 | 59 | describe('Exports', function () { 60 | it('should properly resolve package paths', function () { 61 | Object.entries(packages).forEach(([packageName, packagePath]) => { 62 | const resolvedPath = 63 | typeof packagePath === 'string' 64 | ? path.resolve(context, packagePath) 65 | : packagePath; 66 | assert.ok( 67 | api[/** @type PackageNames */ (packageName)] === resolvedPath, 68 | `Package path not valid for "${packageName}", got "${ 69 | api[/** @type PackageNames */ (packageName)] 70 | }"` 71 | ); 72 | }); 73 | }); 74 | 75 | it('should properly resolve package paths for `node:` protocol', function () { 76 | Object.entries(packages).forEach(([packageName, packagePath]) => { 77 | const resolvedPath = 78 | typeof packagePath === 'string' 79 | ? path.resolve(context, packagePath) 80 | : packagePath; 81 | assert.ok( 82 | api[/** @type PackageNames */ (packageName)] === resolvedPath, 83 | `Package path not valid for "node:${packageName}", got "${ 84 | api[`node:${/** @type PackageNames */ (packageName)}`] 85 | }"` 86 | ); 87 | }); 88 | }); 89 | }); 90 | 91 | describe('`url` additional exports', function () { 92 | it('url.domainToASCII', function () { 93 | const domainWithASCII = [ 94 | ['ıíd', 'xn--d-iga7r'], 95 | ['يٴ', 'xn--mhb8f'], 96 | ['www.ϧƽəʐ.com', 'www.xn--cja62apfr6c.com'], 97 | ['новини.com', 'xn--b1amarcd.com'], 98 | ['名がドメイン.com', 'xn--v8jxj3d1dzdz08w.com'], 99 | ['افغانستا.icom.museum', 'xn--mgbaal8b0b9b2b.icom.museum'], 100 | ['الجزائر.icom.fake', 'xn--lgbbat1ad8j.icom.fake'], 101 | ['भारत.org', 'xn--h2brj9c.org'] 102 | ]; 103 | 104 | domainWithASCII.forEach((pair) => { 105 | const domain = pair[0]; 106 | const ascii = pair[1]; 107 | const domainConvertedToASCII = url.domainToASCII(domain); 108 | assert.strictEqual(domainConvertedToASCII, ascii); 109 | }); 110 | }); 111 | 112 | it('url.domainToUnicode', function () { 113 | const domainWithASCII = [ 114 | ['ıíd', 'xn--d-iga7r'], 115 | ['يٴ', 'xn--mhb8f'], 116 | ['www.ϧƽəʐ.com', 'www.xn--cja62apfr6c.com'], 117 | ['новини.com', 'xn--b1amarcd.com'], 118 | ['名がドメイン.com', 'xn--v8jxj3d1dzdz08w.com'], 119 | ['افغانستا.icom.museum', 'xn--mgbaal8b0b9b2b.icom.museum'], 120 | ['الجزائر.icom.fake', 'xn--lgbbat1ad8j.icom.fake'], 121 | ['भारत.org', 'xn--h2brj9c.org'] 122 | ]; 123 | 124 | domainWithASCII.forEach((pair) => { 125 | const domain = pair[0]; 126 | const ascii = pair[1]; 127 | const domainConvertedToASCII = url.domainToUnicode(domain); 128 | assert.strictEqual(domainConvertedToASCII, ascii); 129 | }); 130 | }); 131 | 132 | it('url.pathToFileURL', function () { 133 | { 134 | const fileURL = url.pathToFileURL('test/').href; 135 | assert.ok(fileURL.startsWith('file:///')); 136 | assert.ok(fileURL.endsWith('/')); 137 | } 138 | { 139 | const fileURL = url.pathToFileURL('test\\').href; 140 | assert.ok(fileURL.startsWith('file:///')); 141 | assert.ok(fileURL.endsWith('%5C')); 142 | } 143 | { 144 | const fileURL = url.pathToFileURL('test/%').href; 145 | assert.ok(fileURL.includes('%25')); 146 | } 147 | { 148 | const fileURL = url.pathToFileURL('\\\\nas\\share\\path.txt').href; 149 | assert.ok( 150 | /file:\/\/.+%5C%5Cnas%5Cshare%5Cpath\.txt$/.test(fileURL) 151 | ); 152 | } 153 | { 154 | const testCases = [ 155 | { path: '/foo', expected: 'file:///foo' }, 156 | { path: '/FOO', expected: 'file:///FOO' }, 157 | { path: '/dir/foo', expected: 'file:///dir/foo' }, 158 | { path: '/dir/', expected: 'file:///dir/' }, 159 | { path: '/foo.mjs', expected: 'file:///foo.mjs' }, 160 | { path: '/foo bar', expected: 'file:///foo%20bar' }, 161 | { path: '/foo?bar', expected: 'file:///foo%3Fbar' }, 162 | { path: '/foo#bar', expected: 'file:///foo%23bar' }, 163 | { path: '/foo&bar', expected: 'file:///foo&bar' }, 164 | { path: '/foo=bar', expected: 'file:///foo=bar' }, 165 | { path: '/foo:bar', expected: 'file:///foo:bar' }, 166 | { path: '/foo;bar', expected: 'file:///foo;bar' }, 167 | { path: '/foo%bar', expected: 'file:///foo%25bar' }, 168 | { path: '/foo\\bar', expected: 'file:///foo%5Cbar' }, 169 | { path: '/foo\bbar', expected: 'file:///foo%08bar' }, 170 | { path: '/foo\tbar', expected: 'file:///foo%09bar' }, 171 | { path: '/foo\nbar', expected: 'file:///foo%0Abar' }, 172 | { path: '/foo\rbar', expected: 'file:///foo%0Dbar' }, 173 | { path: '/fóóbàr', expected: 'file:///f%C3%B3%C3%B3b%C3%A0r' }, 174 | { path: '/€', expected: 'file:///%E2%82%AC' }, 175 | { path: '/🚀', expected: 'file:///%F0%9F%9A%80' } 176 | ]; 177 | 178 | for (const { path, expected } of testCases) { 179 | const actual = url.pathToFileURL(path).href; 180 | assert.strictEqual(actual, expected); 181 | } 182 | } 183 | }); 184 | 185 | it('url.fileURLToPath', function () { 186 | assert.throws(() => url.fileURLToPath('https://a/b/c'), /TypeError/); 187 | { 188 | const withHost = new URL('file://host/a'); 189 | // @ts-ignore 190 | assert.throws(() => url.fileURLToPath(withHost), /TypeError/); 191 | } 192 | assert.throws(() => url.fileURLToPath('file:///a%2F/'), /TypeError/); 193 | { 194 | const testCases = [ 195 | { path: '/foo', fileURL: 'file:///foo' }, 196 | { path: '/FOO', fileURL: 'file:///FOO' }, 197 | { path: '/dir/foo', fileURL: 'file:///dir/foo' }, 198 | { path: '/dir/', fileURL: 'file:///dir/' }, 199 | { path: '/foo.mjs', fileURL: 'file:///foo.mjs' }, 200 | { path: '/foo bar', fileURL: 'file:///foo%20bar' }, 201 | { path: '/foo?bar', fileURL: 'file:///foo%3Fbar' }, 202 | { path: '/foo#bar', fileURL: 'file:///foo%23bar' }, 203 | { path: '/foo&bar', fileURL: 'file:///foo&bar' }, 204 | { path: '/foo=bar', fileURL: 'file:///foo=bar' }, 205 | { path: '/foo:bar', fileURL: 'file:///foo:bar' }, 206 | { path: '/foo;bar', fileURL: 'file:///foo;bar' }, 207 | { path: '/foo%bar', fileURL: 'file:///foo%25bar' }, 208 | { path: '/foo\\bar', fileURL: 'file:///foo%5Cbar' }, 209 | { path: '/foo\bbar', fileURL: 'file:///foo%08bar' }, 210 | { path: '/foo\tbar', fileURL: 'file:///foo%09bar' }, 211 | { path: '/foo\nbar', fileURL: 'file:///foo%0Abar' }, 212 | { path: '/foo\rbar', fileURL: 'file:///foo%0Dbar' }, 213 | { path: '/fóóbàr', fileURL: 'file:///f%C3%B3%C3%B3b%C3%A0r' }, 214 | { path: '/€', fileURL: 'file:///%E2%82%AC' }, 215 | { path: '/🚀', fileURL: 'file:///%F0%9F%9A%80' } 216 | ]; 217 | 218 | for (const { path, fileURL } of testCases) { 219 | const fromString = url.fileURLToPath(fileURL); 220 | assert.strictEqual(fromString, path); 221 | const fromURL = url.fileURLToPath(new URL(fileURL)); 222 | assert.strictEqual(fromURL, path); 223 | } 224 | } 225 | }); 226 | 227 | it('url.format, URL instance passed as first argument', function () { 228 | const myURL = new URL('http://xn--lck1c3crb1723bpq4a.com/a?a=b#c'); 229 | 230 | assert.strictEqual( 231 | url.format(myURL), 232 | 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c' 233 | ); 234 | 235 | assert.strictEqual( 236 | url.format(myURL, {}), 237 | 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c' 238 | ); 239 | 240 | [true, 1, 'test', Infinity].forEach((value) => { 241 | // @ts-ignore 242 | assert.throws(() => url.format(myURL, value), TypeError); 243 | }); 244 | 245 | /* 246 | * Any falsy value other than undefined will be treated as false. 247 | * Any truthy value will be treated as true. 248 | */ 249 | 250 | assert.strictEqual( 251 | url.format(myURL, { fragment: false }), 252 | 'http://xn--lck1c3crb1723bpq4a.com/a?a=b' 253 | ); 254 | 255 | assert.strictEqual( 256 | // @ts-ignore 257 | url.format(myURL, { fragment: '' }), 258 | 'http://xn--lck1c3crb1723bpq4a.com/a?a=b' 259 | ); 260 | 261 | assert.strictEqual( 262 | // @ts-ignore 263 | url.format(myURL, { fragment: 0 }), 264 | 'http://xn--lck1c3crb1723bpq4a.com/a?a=b' 265 | ); 266 | 267 | assert.strictEqual( 268 | // @ts-ignore 269 | url.format(myURL, { fragment: 1 }), 270 | 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c' 271 | ); 272 | 273 | assert.strictEqual( 274 | // @ts-ignore 275 | url.format(myURL, { fragment: {} }), 276 | 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c' 277 | ); 278 | 279 | assert.strictEqual( 280 | // @ts-ignore 281 | url.format(myURL, { search: false }), 282 | 'http://xn--lck1c3crb1723bpq4a.com/a#c' 283 | ); 284 | 285 | assert.strictEqual( 286 | // @ts-ignore 287 | url.format(myURL, { search: '' }), 288 | 'http://xn--lck1c3crb1723bpq4a.com/a#c' 289 | ); 290 | 291 | assert.strictEqual( 292 | // @ts-ignore 293 | url.format(myURL, { search: 0 }), 294 | 'http://xn--lck1c3crb1723bpq4a.com/a#c' 295 | ); 296 | 297 | assert.strictEqual( 298 | // @ts-ignore 299 | url.format(myURL, { search: 1 }), 300 | 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c' 301 | ); 302 | 303 | assert.strictEqual( 304 | // @ts-ignore 305 | url.format(myURL, { search: {} }), 306 | 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c' 307 | ); 308 | }); 309 | }); 310 | 311 | describe('`querystring` additional exports', function () { 312 | describe('escape', function () { 313 | it('does basic escaping', function () { 314 | // @ts-ignore 315 | assert.deepEqual(qs.escape(5), '5'); 316 | assert.deepEqual(qs.escape('test'), 'test'); 317 | // @ts-ignore 318 | assert.deepEqual(qs.escape({}), '%5Bobject%20Object%5D'); 319 | // @ts-ignore 320 | assert.deepEqual(qs.escape([5, 10]), '5%2C10'); 321 | assert.deepEqual(qs.escape('Ŋōđĕ'), '%C5%8A%C5%8D%C4%91%C4%95'); 322 | assert.deepEqual( 323 | qs.escape('testŊōđĕ'), 324 | 'test%C5%8A%C5%8D%C4%91%C4%95' 325 | ); 326 | assert.deepEqual(qs.escape('�test'), '%EF%BF%BDtest'); 327 | }); 328 | 329 | it('using toString for objects', function () { 330 | assert.strictEqual( 331 | // @ts-ignore 332 | qs.escape({ 333 | test: 5, 334 | toString: () => 'test', 335 | valueOf: () => 10 336 | }), 337 | 'test' 338 | ); 339 | }); 340 | 341 | it('toString is not callable, must throw an error', function () { 342 | // @ts-ignore 343 | assert.throws(() => qs.escape({ toString: 5 })); 344 | }); 345 | 346 | it('should use valueOf instead of non-callable toString', function () { 347 | assert.strictEqual( 348 | // @ts-ignore 349 | qs.escape({ toString: 5, valueOf: () => 'test' }), 350 | 'test' 351 | ); 352 | }); 353 | 354 | it('throws when given Symbol', function () { 355 | try { 356 | // @ts-ignore 357 | qs.escape(Symbol('test')); 358 | } catch (error) { 359 | if ( 360 | error instanceof TypeError && 361 | /[Ss]ymbol.+string/.test(error.message) 362 | ) { 363 | assert.ok(true); 364 | } else { 365 | throw error; 366 | } 367 | } 368 | }); 369 | }); 370 | 371 | describe('unescape', function () { 372 | it('does basic unescaping', function () { 373 | assert.deepEqual(qs.unescape('5'), '5'); 374 | assert.deepEqual(qs.unescape('test'), 'test'); 375 | assert.deepEqual( 376 | qs.unescape('%5Bobject%20Object%5D'), 377 | '[object Object]' 378 | ); 379 | assert.deepEqual(qs.unescape('5%2C10'), '5,10'); 380 | assert.deepEqual(qs.unescape('%C5%8A%C5%8D%C4%91%C4%95'), 'Ŋōđĕ'); 381 | assert.deepEqual( 382 | qs.unescape('test%C5%8A%C5%8D%C4%91%C4%95'), 383 | 'testŊōđĕ' 384 | ); 385 | assert.deepEqual(qs.unescape('%EF%BF%BDtest'), '�test'); 386 | }); 387 | 388 | it('using JSON objects', function () { 389 | assert.strictEqual( 390 | qs.unescape( 391 | JSON.stringify({ 392 | test: 5, 393 | toString: () => 'test', 394 | valueOf: () => 10 395 | }) 396 | ), 397 | '{"test":5}' 398 | ); 399 | }); 400 | 401 | it('throws when given Symbol', function () { 402 | try { 403 | // @ts-ignore 404 | qs.unescape(Symbol('test')); 405 | } catch (error) { 406 | if ( 407 | error instanceof TypeError && 408 | /[Ss]ymbol.+string/.test(error.message) 409 | ) { 410 | assert.ok(true); 411 | } else { 412 | throw error; 413 | } 414 | } 415 | }); 416 | }); 417 | }); 418 | 419 | describe('`process` additional exports', function () { 420 | it('has exports for browser environment', function () { 421 | assert.equal(_process.title, 'browser'); 422 | assert.equal(_process.browser, true); 423 | assert.equal(_process.arch, 'browser'); 424 | assert.equal(_process.platform, 'browser'); 425 | assert.ok(Array.isArray(_process.execArgv)); 426 | assert.ok(typeof _process.emitWarning !== 'undefined'); 427 | }); 428 | }); 429 | 430 | const nodeVersion = parseNodeVersion(process.version); 431 | const shouldBundle = nodeVersion.major >= 12; 432 | const shouldBundleESM = nodeVersion.major >= 16; 433 | 434 | describe('Bundling', function () { 435 | this.timeout(60000 * 2); 436 | 437 | const cwd = path.resolve(__dirname, '../example'); 438 | 439 | before(async function () { 440 | if (shouldBundle) { 441 | await execa('npm', ['install'], { cwd }); 442 | await execa('npm', ['run', 'build'], { cwd }); 443 | } 444 | }); 445 | 446 | it('bundles for Webpack', async function () { 447 | const bundles = []; 448 | if (shouldBundle) { 449 | bundles.push(execa('npm', ['run', 'build:webpack:cjs'], { cwd })); 450 | } 451 | if (shouldBundleESM) { 452 | /* Bundles.push(execa('npm', ['run', 'build:webpack:esm'], { cwd }));*/ 453 | } 454 | await Promise.all(bundles); 455 | assert.ok(true); 456 | }); 457 | 458 | it('bundles for Rollup', async function () { 459 | const bundles = []; 460 | if (shouldBundle) { 461 | bundles.push(execa('npm', ['run', 'build:rollup:cjs'], { cwd })); 462 | } 463 | if (shouldBundleESM) { 464 | bundles.push(execa('npm', ['run', 'build:rollup:esm'], { cwd })); 465 | } 466 | await Promise.all(bundles); 467 | assert.ok(true); 468 | }); 469 | 470 | it('bundles for Vite', async function () { 471 | const bundles = []; 472 | if (shouldBundle) { 473 | bundles.push(execa('npm', ['run', 'build:vite:cjs'], { cwd })); 474 | } 475 | if (shouldBundleESM) { 476 | bundles.push(execa('npm', ['run', 'build:vite:esm'], { cwd })); 477 | } 478 | await Promise.all(bundles); 479 | assert.ok(true); 480 | }); 481 | 482 | it('bundles for esbuild', async function () { 483 | const bundles = []; 484 | if (shouldBundle) { 485 | bundles.push(execa('npm', ['run', 'build:esbuild:cjs'], { cwd })); 486 | } 487 | if (shouldBundleESM) { 488 | bundles.push(execa('npm', ['run', 'build:esbuild:esm'], { cwd })); 489 | } 490 | await Promise.all(bundles); 491 | assert.ok(true); 492 | }); 493 | 494 | it('bundles for Browserify', async function () { 495 | const bundles = []; 496 | if (shouldBundle) { 497 | bundles.push( 498 | execa('npm', ['run', 'build:browserify:cjs'], { cwd }) 499 | ); 500 | } 501 | if (shouldBundleESM) { 502 | bundles.push( 503 | execa('npm', ['run', 'build:browserify:esm'], { cwd }) 504 | ); 505 | } 506 | await Promise.all(bundles); 507 | assert.ok(true); 508 | }); 509 | }); 510 | --------------------------------------------------------------------------------