diff --git a/js/common/.npmignore b/js/common/.npmignore index 64e6b50c6f..37fefbcfcb 100644 --- a/js/common/.npmignore +++ b/js/common/.npmignore @@ -1,5 +1,6 @@ /.vscode/ +build.js webpack.config.js typedoc.json tsconfig.json diff --git a/js/common/build.js b/js/common/build.js new file mode 100644 index 0000000000..cf459f5efa --- /dev/null +++ b/js/common/build.js @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import {execSync} from 'node:child_process'; +import {writeFileSync} from 'node:fs'; +import {resolve, dirname} from 'node:path'; +import {fileURLToPath} from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +// build the following folders: +// - dist/cjs +// - dist/esm +execSync('npm run build:cjs', {shell: true, stdio: 'inherit', cwd: __dirname}); +execSync('npm run build:esm', {shell: true, stdio: 'inherit', cwd: __dirname}); + +// generate package.json files under each of the dist folders for commonJS and ESModule +// this trick allows typescript to import this package as different module type +// see also: https://evertpot.com/universal-commonjs-esm-typescript-packages/ +writeFileSync(resolve(__dirname, './dist/cjs', 'package.json'), '{"type": "commonjs"}'); +writeFileSync(resolve(__dirname, './dist/esm', 'package.json'), '{"type": "module"}'); + +// launch webpack to generate bundles +execSync('npm run build:bundles', {shell: true, stdio: 'inherit', cwd: __dirname}); diff --git a/js/common/lib/backend-impl.ts b/js/common/lib/backend-impl.ts index 49e363e398..ef8c23c5b6 100644 --- a/js/common/lib/backend-impl.ts +++ b/js/common/lib/backend-impl.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import {Backend} from './backend'; +import {Backend} from './backend.js'; interface BackendInfo { backend: Backend; diff --git a/js/common/lib/backend.ts b/js/common/lib/backend.ts index 3f1bbc9fa7..226abaf033 100644 --- a/js/common/lib/backend.ts +++ b/js/common/lib/backend.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import {InferenceSession} from './inference-session'; -import {OnnxValue} from './onnx-value'; +import {InferenceSession} from './inference-session.js'; +import {OnnxValue} from './onnx-value.js'; /** * @internal @@ -46,4 +46,4 @@ export interface Backend { Promise; } -export {registerBackend} from './backend-impl'; +export {registerBackend} from './backend-impl.js'; diff --git a/js/common/lib/env-impl.ts b/js/common/lib/env-impl.ts index 4d6c663096..c3e96d864d 100644 --- a/js/common/lib/env-impl.ts +++ b/js/common/lib/env-impl.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import {Env} from './env'; -import {version} from './version'; +import {Env} from './env.js'; +import {version} from './version.js'; type LogLevelType = Env['logLevel']; diff --git a/js/common/lib/env.ts b/js/common/lib/env.ts index 7bd91cf5ad..f1f8a8aad5 100644 --- a/js/common/lib/env.ts +++ b/js/common/lib/env.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import {env as envImpl} from './env-impl'; +import {env as envImpl} from './env-impl.js'; export declare namespace Env { export type WasmPrefixOrFilePaths = string|{ diff --git a/js/common/lib/index.ts b/js/common/lib/index.ts index 81d380354b..85df1747f8 100644 --- a/js/common/lib/index.ts +++ b/js/common/lib/index.ts @@ -17,8 +17,8 @@ * @packageDocumentation */ -export * from './backend'; -export * from './env'; -export * from './inference-session'; -export * from './tensor'; -export * from './onnx-value'; +export * from './backend.js'; +export * from './env.js'; +export * from './inference-session.js'; +export * from './tensor.js'; +export * from './onnx-value.js'; diff --git a/js/common/lib/inference-session-impl.ts b/js/common/lib/inference-session-impl.ts index 65be994ef5..2403b82ea8 100644 --- a/js/common/lib/inference-session-impl.ts +++ b/js/common/lib/inference-session-impl.ts @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import {SessionHandler} from './backend'; -import {resolveBackend} from './backend-impl'; -import {InferenceSession as InferenceSessionInterface} from './inference-session'; -import {OnnxValue} from './onnx-value'; -import {Tensor} from './tensor'; +import {resolveBackend} from './backend-impl.js'; +import {SessionHandler} from './backend.js'; +import {InferenceSession as InferenceSessionInterface} from './inference-session.js'; +import {OnnxValue} from './onnx-value.js'; +import {Tensor} from './tensor.js'; type SessionOptions = InferenceSessionInterface.SessionOptions; type RunOptions = InferenceSessionInterface.RunOptions; diff --git a/js/common/lib/inference-session.ts b/js/common/lib/inference-session.ts index e9c3d2da80..a79bcd7ebc 100644 --- a/js/common/lib/inference-session.ts +++ b/js/common/lib/inference-session.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import {InferenceSession as InferenceSessionImpl} from './inference-session-impl'; -import {OnnxValue} from './onnx-value'; +import {InferenceSession as InferenceSessionImpl} from './inference-session-impl.js'; +import {OnnxValue} from './onnx-value.js'; /* eslint-disable @typescript-eslint/no-redeclare */ diff --git a/js/common/lib/onnx-value.ts b/js/common/lib/onnx-value.ts index 53c493dc33..29b9d64d9b 100644 --- a/js/common/lib/onnx-value.ts +++ b/js/common/lib/onnx-value.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import {Tensor} from './tensor'; +import {Tensor} from './tensor.js'; type NonTensorType = never; diff --git a/js/common/lib/tensor-conversion-impl.ts b/js/common/lib/tensor-conversion-impl.ts index fe8ed3dcb4..22397321e8 100644 --- a/js/common/lib/tensor-conversion-impl.ts +++ b/js/common/lib/tensor-conversion-impl.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import {Tensor} from './tensor'; -import {TensorToDataUrlOptions, TensorToImageDataOptions} from './tensor-conversion'; +import {TensorToDataUrlOptions, TensorToImageDataOptions} from './tensor-conversion.js'; +import {Tensor} from './tensor.js'; /** * implementation of Tensor.toDataURL() diff --git a/js/common/lib/tensor-conversion.ts b/js/common/lib/tensor-conversion.ts index a1202f2f03..4542b3b4a7 100644 --- a/js/common/lib/tensor-conversion.ts +++ b/js/common/lib/tensor-conversion.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import {OptionsFormat, OptionsNormalizationParameters, OptionsTensorLayout} from './tensor-factory'; +import {OptionsFormat, OptionsNormalizationParameters, OptionsTensorLayout} from './tensor-factory.js'; export interface TensorToDataUrlOptions extends OptionsTensorLayout, OptionsFormat, OptionsNormalizationParameters {} diff --git a/js/common/lib/tensor-factory-impl.ts b/js/common/lib/tensor-factory-impl.ts index 94a5fa6c25..af72ac51e7 100644 --- a/js/common/lib/tensor-factory-impl.ts +++ b/js/common/lib/tensor-factory-impl.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import {Tensor, TypedTensor} from './tensor'; -import {OptionsDimensions, OptionsFormat, OptionsNormalizationParameters, OptionsTensorFormat, OptionsTensorLayout, TensorFromImageBitmapOptions, TensorFromImageDataOptions, TensorFromImageElementOptions, TensorFromUrlOptions} from './tensor-factory'; +import {OptionsDimensions, OptionsFormat, OptionsNormalizationParameters, OptionsTensorFormat, OptionsTensorLayout, TensorFromImageBitmapOptions, TensorFromImageDataOptions, TensorFromImageElementOptions, TensorFromUrlOptions} from './tensor-factory.js'; +import {Tensor, TypedTensor} from './tensor.js'; interface BufferToTensorOptions extends OptionsDimensions, OptionsTensorLayout, OptionsNormalizationParameters, OptionsFormat, OptionsTensorFormat {} diff --git a/js/common/lib/tensor-factory.ts b/js/common/lib/tensor-factory.ts index d92ccd9472..3eac33c0e8 100644 --- a/js/common/lib/tensor-factory.ts +++ b/js/common/lib/tensor-factory.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import {TypedTensor} from './tensor'; +import {TypedTensor} from './tensor.js'; export type ImageFormat = 'RGB'|'RGBA'|'BGR'|'RBG'; export type ImageTensorLayout = 'NHWC'|'NCHW'; diff --git a/js/common/lib/tensor-impl.ts b/js/common/lib/tensor-impl.ts index 7fd2324829..a133ce5d26 100644 --- a/js/common/lib/tensor-impl.ts +++ b/js/common/lib/tensor-impl.ts @@ -1,12 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import {Tensor as TensorInterface} from './tensor'; -import {TensorToDataUrlOptions, TensorToImageDataOptions} from './tensor-conversion'; -import {tensorToDataURL, tensorToImageData} from './tensor-conversion-impl'; -import {TensorFromImageBitmapOptions, TensorFromImageDataOptions, TensorFromImageElementOptions, TensorFromUrlOptions} from './tensor-factory'; -import {tensorFromImage} from './tensor-factory-impl'; -import {calculateSize, tensorReshape} from './tensor-utils-impl'; +import {tensorToDataURL, tensorToImageData} from './tensor-conversion-impl.js'; +import {TensorToDataUrlOptions, TensorToImageDataOptions} from './tensor-conversion.js'; +import {tensorFromImage} from './tensor-factory-impl.js'; +import {TensorFromImageBitmapOptions, TensorFromImageDataOptions, TensorFromImageElementOptions, TensorFromUrlOptions} from './tensor-factory.js'; +import {calculateSize, tensorReshape} from './tensor-utils-impl.js'; +import {Tensor as TensorInterface} from './tensor.js'; type TensorType = TensorInterface.Type; type TensorDataType = TensorInterface.DataType; diff --git a/js/common/lib/tensor-utils-impl.ts b/js/common/lib/tensor-utils-impl.ts index dc6e7f40cf..8a259b2361 100644 --- a/js/common/lib/tensor-utils-impl.ts +++ b/js/common/lib/tensor-utils-impl.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import {Tensor} from './tensor'; +import {Tensor} from './tensor.js'; /** * calculate size from dims. diff --git a/js/common/lib/tensor-utils.ts b/js/common/lib/tensor-utils.ts index 49621b976b..b24075aad2 100644 --- a/js/common/lib/tensor-utils.ts +++ b/js/common/lib/tensor-utils.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import {Tensor, TypedTensor} from './tensor'; -import {ConversionUtils} from './tensor-conversion'; +import {ConversionUtils} from './tensor-conversion.js'; +import {Tensor, TypedTensor} from './tensor.js'; interface Properties { /** diff --git a/js/common/lib/tensor.ts b/js/common/lib/tensor.ts index 7eed73db5b..d827f50272 100644 --- a/js/common/lib/tensor.ts +++ b/js/common/lib/tensor.ts @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import {TensorFactory} from './tensor-factory'; -import {Tensor as TensorImpl} from './tensor-impl'; -import {TypedTensorUtils} from './tensor-utils'; +import {TensorFactory} from './tensor-factory.js'; +import {Tensor as TensorImpl} from './tensor-impl.js'; +import {TypedTensorUtils} from './tensor-utils.js'; /* eslint-disable @typescript-eslint/no-redeclare */ diff --git a/js/common/package.json b/js/common/package.json index 447d03b38f..80bef5fe6c 100644 --- a/js/common/package.json +++ b/js/common/package.json @@ -1,27 +1,32 @@ { "license": "MIT", - "unpkg": "dist/ort-common.min.js", + "type": "module", "name": "onnxruntime-common", + "version": "1.16.0", "repository": { "url": "https://github.com/Microsoft/onnxruntime.git", "type": "git" }, "author": "fs-eire", - "module": "dist/lib/index.js", - "version": "1.16.0", - "jsdelivr": "dist/ort-common.min.js", "scripts": { - "prepare": "tsc && webpack" + "build:cjs": "npx tsc --module commonjs --outDir ./dist/cjs", + "build:esm": "npx tsc", + "build:bundles": "webpack", + "build": "node ./build.js", + "prepare": "npm run build" + }, + "devDependencies": { + "typedoc": "^0.23.22" + }, + "main": "dist/cjs/index.js", + "exports": { + "require": "./dist/cjs/index.js", + "import": "./dist/esm/index.js" }, "keywords": [ "ONNX", "ONNXRuntime", "ONNX Runtime" ], - "devDependencies": { - "typedoc": "^0.23.22" - }, - "main": "dist/ort-common.node.js", - "types": "dist/lib/index.d.ts", "description": "ONNXRuntime JavaScript API library" } diff --git a/js/common/tsconfig.json b/js/common/tsconfig.json index 9e1ac941c4..3d7adcbf23 100644 --- a/js/common/tsconfig.json +++ b/js/common/tsconfig.json @@ -1,7 +1,9 @@ { "extends": "../tsconfig.json", "compilerOptions": { - "outDir": "dist/lib", + "outDir": "./dist/esm", + "declaration": true, + "declarationMap": true, "esModuleInterop": false, "noUnusedParameters": true, }, diff --git a/js/common/webpack.config.js b/js/common/webpack.config.js index e5c4e417bc..b9d1536f4b 100644 --- a/js/common/webpack.config.js +++ b/js/common/webpack.config.js @@ -3,98 +3,65 @@ 'use strict'; -const path = require('path'); -const webpack = require('webpack'); -const TerserPlugin = require("terser-webpack-plugin"); - -function terserEcmaVersionFromWebpackTarget(target) { - switch (target) { - case 'es5': - return 5; - case 'es6': - case 'es2015': - return 2015; - case 'es2017': - return 2017; - default: - throw new RangeError(`not supported ECMA version: ${target}`); - } -} - -function addCopyrightBannerPlugin(mode, target) { - const VERSION = require(path.join(__dirname, 'package.json')).version; - const COPYRIGHT_BANNER = `/*! - * ONNX Runtime Common v${VERSION} - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */`; - - if (mode === 'production') { - return new TerserPlugin({ - extractComments: false, - terserOptions: { - ecma: terserEcmaVersionFromWebpackTarget(target), - format: { - preamble: COPYRIGHT_BANNER, - comments: false, - }, - compress: { - passes: 2 - } - } - }); - } else { - return new webpack.BannerPlugin({ banner: COPYRIGHT_BANNER, raw: true }); - } -} +import webpack from 'webpack'; +import {resolve} from 'node:path'; +import {DEFAULT_ES_VERSION, addCopyrightBannerPlugin} from '../webpack.shared.mjs'; function buildConfig({ - suffix = '', - format = 'umd', - target = 'es2017', - mode = 'production', - devtool = 'source-map' + suffix = '.js', // '.js', '.min.js', ... + format = 'umd', // 'umd', 'commonjs' + target = 'web', // 'web', 'node' + esVersion = DEFAULT_ES_VERSION, // 'es5', 'es6', ... + mode = 'production', // 'development', 'production' + devtool = 'source-map' // 'inline-source-map', 'source-map' }) { + // output file name + const filename = `ort-common${suffix}`; + + // variable name of the exported object. + // - set to 'ort' when building 'umd' format. + // - set to undefined when building other formats (commonjs/module) + const exportName = format === 'umd' ? 'ort' : undefined; + return { - target: [format === 'commonjs' ? 'node' : 'web', target], - entry: path.resolve(__dirname, 'lib/index.ts'), + target: [target, esVersion], + entry: resolve('./lib/index.ts'), output: { - path: path.resolve(__dirname, 'dist'), - filename: `ort-common${suffix}.js`, - library: { - name: format === 'commonjs' ? undefined : 'ort', - type: format - } + path: resolve('./dist'), + filename, + library: {name: exportName, type: format}, + }, + resolve: { + extensions: ['.ts', '.js'], + extensionAlias: {'.js': ['.ts', '.js']}, }, - resolve: { extensions: ['.ts', '.js'] }, plugins: [ - new webpack.WatchIgnorePlugin({ paths: [/\.js$/, /\.d\.ts$/] }), - addCopyrightBannerPlugin(mode, target), + new webpack.WatchIgnorePlugin({paths: [/\.js$/, /\.d\.ts$/]}), + addCopyrightBannerPlugin(mode, 'common', esVersion), ], module: { rules: [{ - test: /\.tsx?$/, - use: [ - { - loader: 'ts-loader', - options: { - compilerOptions: { target } - } - } - ] + test: /\.ts$/, + use: [{ + loader: 'ts-loader', + options: {compilerOptions: {target: esVersion}}, + }] }] }, - mode: mode, - devtool: devtool, + mode, + devtool, }; } -module.exports = (env, argv) => { +export default (env, argv) => { return [ - buildConfig({ suffix: '.es5.min', target: 'es5' }), - buildConfig({ suffix: '.es6.min', target: 'es6' }), - buildConfig({ suffix: '.min' }), - buildConfig({ mode: 'development', devtool: 'inline-source-map' }), - buildConfig({ format: 'commonjs', suffix: '.node' }), + buildConfig({suffix: '.es5.min.js', target: 'web', esVersion: 'es5'}), + buildConfig({suffix: '.min.js'}), + buildConfig({mode: 'development', devtool: 'inline-source-map'}), + buildConfig({ + suffix: '.node.cjs', + target: 'node', + format: 'commonjs', + }), ]; }; diff --git a/js/tsconfig.json b/js/tsconfig.json index dfee3c0567..d199cb9d0a 100644 --- a/js/tsconfig.json +++ b/js/tsconfig.json @@ -2,7 +2,6 @@ "compilerOptions": { "module": "ES2015", "moduleResolution": "node16", - "declaration": true, "esModuleInterop": true, "target": "ES2020", "lib": ["ES2020", "ESNext.BigInt", "dom"], diff --git a/js/web/tsconfig.json b/js/web/tsconfig.json index 865c393b5b..4256d7dbf0 100644 --- a/js/web/tsconfig.json +++ b/js/web/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "module": "CommonJS", "downlevelIteration": true, + "declaration": true, "declarationDir": "./types", "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] }, diff --git a/js/webpack.shared.mjs b/js/webpack.shared.mjs new file mode 100644 index 0000000000..d1b95722ff --- /dev/null +++ b/js/webpack.shared.mjs @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import webpack from 'webpack'; +import TerserPlugin from 'terser-webpack-plugin'; +import {resolve, dirname} from 'node:path'; +import {readFileSync} from 'node:fs'; +import {fileURLToPath} from 'node:url'; + +/** + * ECMAScript version for default onnxruntime JavaScript API builds + */ +export const DEFAULT_ES_VERSION = 'es2017'; + +// how to use "__dirname" in ESM: https://shramko.dev/blog/dirname-error +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const terserEcmaVersionFromWebpackEsVersion = (target) => { + switch (target) { + case 'es5': + return 5; + case 'es6': + case 'es2015': + return 2015; + case 'es2017': + return 2017; + default: + throw new RangeError(`not supported ECMA version: ${target}`); + } +}; + +const getPackageFullName = (name) => { + switch (name) { + case 'common': + return `ONNX Runtime Common`; + case 'node': + return `ONNX Runtime Node.js Binding`; + case 'web': + return `ONNX Runtime Web`; + case 'react-native': + return `ONNX Runtime React-native`; + default: + throw new RangeError(`unknown package name: ${name}`); + } +}; + +/** + * Get package version by reading the file "package.json" under the package folder + * @param {'common'|'node'|'web'|'react-native'} name - the package name + * @returns a string representing the package version + */ +const getPackageVersion = (name) => { + const normalizedName = name.replace('-', '_'); + const packageJsonFileContent = readFileSync(resolve(__dirname, normalizedName, 'package.json')); + const packageJson = JSON.parse(packageJsonFileContent); + return packageJson.version; +}; + +/** + * + * @param {'development'|'production'} mode - specify webpack build mode + * @param {'common'|'node'|'web'|'react-native'} packageName - specify the name of the package + * @param {'es5'|'es6'|'es2015'|'es2017'} esVersion - specify the ECMAScript version + * @returns + */ +export const addCopyrightBannerPlugin = (mode, packageName, esVersion) => { + const COPYRIGHT_BANNER = `/*! + * ${getPackageFullName(packageName)} v${getPackageVersion(packageName)} + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */`; + + if (mode === 'production') { + // in 'production' mode, webpack uses terser to minimize the code. + // we set options.format.preamble to make sure terser generates correct copyright banner. + return new TerserPlugin({ + extractComments: false, + terserOptions: { + ecma: terserEcmaVersionFromWebpackEsVersion(esVersion), + format: { + preamble: COPYRIGHT_BANNER, + comments: false, + }, + compress: {passes: 2} + } + }); + } else { + // in 'development' mode, webpack does not minimize the code. + // we use the webpack builtin plugin BannerPlugin to insert the banner. + return new webpack.BannerPlugin({banner: COPYRIGHT_BANNER, raw: true}); + } +};