From 016f831539dba70d3b70f56bb889f9c73914c239 Mon Sep 17 00:00:00 2001 From: matthew44-mappable <155086725+matthew44-mappable@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:05:48 +0300 Subject: [PATCH] Fix ESM build for package (#36) Fix ESM build with webpack: create JS and CSS bundles. --- .github/workflows/tests.yml | 8 +++- README.md | 17 ++++++-- package-lock.json | 86 +++++++++++++++++++++++++++++++++++++ package.json | 5 ++- webpack.config.js | 46 +++++++++++++++++++- 5 files changed, 153 insertions(+), 9 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ac7e477..0d81cd0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,8 +16,12 @@ jobs: with: node-version-file: '.nvmrc' cache: 'npm' - - - name: Run Tests + - name: Install dependencies run: | npm ci + - name: Build + run: | + APIKEY=${{ secrets.APIKEY }} npm run build + - name: Run Tests + run: | APIKEY=${{ secrets.APIKEY }} npm test diff --git a/README.md b/README.md index 15086dc..e81dd6b 100644 --- a/README.md +++ b/README.md @@ -16,16 +16,20 @@ The package is located in the `dist` folder: - `dist/esm` es6 modules for direct connection in your project - `dist/index.js` Mappable JS Module -Recommended use `@mappable-world/mappable-default-ui-theme` as usual npm package: +Recommended use `@mappable-world/mappable-default-ui-theme` as usual npm package. + +### Usage with npm + +Install `@mappable-world/mappable-default-ui-theme` package from npm: ```sh npm install @mappable-world/mappable-default-ui-theme ``` -and dynamic import +The JS API is loaded dynamically, so the package must be used after the main JS API has loaded: ```js -await mappable.ready; +await mappable.ready; // waiting for the main JS API to load. // ... @@ -36,6 +40,13 @@ const {MMapDefaultMarker} = await import('@mappable-world/mappable-default-ui-th map.addChild(new MMapDefaultMarker(props)); ``` +You also need to import css styles into your project: + +```css +/* index.css */ +@import '@mappable-world/mappable-default-ui-theme/dist/esm/index.css'; +``` + ### Usage without npm You can use CDN with module loading handler in JS API on your page. diff --git a/package-lock.json b/package-lock.json index 276b093..93b7e31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,7 @@ "jest": "29.6.1", "jsdom": "22.1.0", "lodash": "^4.17.21", + "mini-css-extract-plugin": "^2.9.1", "nanospinner": "^1.1.0", "postcss": "^8.4.39", "postcss-loader": "^8.1.1", @@ -4508,6 +4509,13 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-uri": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.2.tgz", + "integrity": "sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row==", + "dev": true, + "license": "MIT" + }, "node_modules/fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", @@ -6695,6 +6703,84 @@ "node": ">=4" } }, + "node_modules/mini-css-extract-plugin": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.1.tgz", + "integrity": "sha512-+Vyi+GCCOHnrJ2VPS+6aPoXN2k2jgUzDRhTFLjjTBn23qyXJXkjUWQgTL+mXpF5/A8ixLdCc6kWsoeOjKGejKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", diff --git a/package.json b/package.json index b3c76a4..21896ee 100644 --- a/package.json +++ b/package.json @@ -3,14 +3,14 @@ "name": "@mappable-world/mappable-default-ui-theme", "description": "Mappable Maps JS API 3.0 example @mappable-world/mappable-default-ui-theme", "types": "./dist/types/index.d.ts", - "main": "./dist/esm/index.js", + "main": "./dist/esm/index.mjs", "scripts": { "test": "jest", "lint": "eslint ./", "build": "npm run build:prod && npm run build:esm", "build:dev": "webpack --mode=development", "build:prod": "webpack --mode=production --node-env=production", - "build:esm": "rm -rf ./dist/esm && tsc --project tsconfig.build.json --target es2018 --module es2022 --outDir dist/esm", + "build:esm": "ESM_BUILD=true webpack --mode=production --node-env=production", "examples": "mappable-cli example --input=example --output=dist/example --readmeFile=example/README.md --templateFile=example/index.html", "watch": "webpack --watch", "start": "webpack serve", @@ -46,6 +46,7 @@ "jest": "29.6.1", "jsdom": "22.1.0", "lodash": "^4.17.21", + "mini-css-extract-plugin": "^2.9.1", "nanospinner": "^1.1.0", "postcss": "^8.4.39", "postcss-loader": "^8.1.1", diff --git a/webpack.config.js b/webpack.config.js index 5ad97cb..73f7ff8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,3 +1,45 @@ +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const path = require('path'); + +const ESM_BUILD = process.env.ESM_BUILD === 'true'; + module.exports = (args, env, dir = process.cwd()) => { - return require('@mappable-world/mappable-cli/webpack.config')(args, env, dir); -} + const coreWebpackModule = require('@mappable-world/mappable-cli/webpack.config')(args, env, dir); + + if (!ESM_BUILD) { + return coreWebpackModule; + } + + return { + ...coreWebpackModule, + experiments: {outputModule: true}, + entry: {index: {import: './src/index.ts', library: {type: 'module'}}}, + output: {...coreWebpackModule.output, path: path.resolve(dir, 'dist/esm')}, + plugins: [new MiniCssExtractPlugin()], + module: { + rules: [ + { + test: /\.(ts|tsx)$/i, + loader: 'ts-loader', + options: { + compilerOptions: {declaration: true, declarationDir: 'dist/types'}, + onlyCompileBundledFiles: true + }, + exclude: ['/node_modules/'] + }, + { + test: /\.css$/i, + use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] + }, + { + test: /\.(eot|ttf|woff|woff2|png|jpg|gif)$/i, + type: 'asset' + }, + { + test: /\.(svg|frag|vert)$/i, + oneOf: [{resourceQuery: /inline/, type: 'asset/inline'}, {type: 'asset/source'}] + } + ] + } + }; +};