From e0dc8fd2e41b186b1db0fd62259f2f1acf4acd13 Mon Sep 17 00:00:00 2001 From: Matt Jennings Date: Mon, 29 Apr 2024 09:09:32 -0500 Subject: [PATCH] feat: provide development build for bundlers, warn on unadded entity (#3032) This adds a "development" build of Excalibur that [bundlers can use](https://webpack.js.org/guides/package-exports/#providing-devtools-or-production-optimizations). It allows us to include code that can be used to help the user during development but still keep it out of production builds as to not affect performance / size. This requires removing the `module` field in package.json in favour of `exports`, which is OK (`exports` essentially supersedes `module`). **There is potential for breaking change** here, as any nested imports e.g. `'excalibur/dist/abc'` would now need to be added to this exports map. But I don't believe there was ever a reason to do that, since everything is exported from the excalibur.js file. (Types are not affected) I've tested this in a Vite project and it works, haven't tested it with Webpack yet. ## Changes: - create development builds of excalibur that bundlers can use in dev mode - show warning in development when Entity hasn't been added to a scene after a few seconds --- CHANGELOG.md | 2 + karma.conf.js | 3 +- package.json | 25 ++++++++++-- src/engine/EntityComponentSystem/Entity.ts | 10 ++++- src/engine/env.d.ts | 11 ++++++ src/engine/globals.d.ts | 6 --- src/engine/tsconfig.json | 5 ++- webpack.config.js | 45 +++++++++++++--------- 8 files changed, 74 insertions(+), 33 deletions(-) create mode 100644 src/engine/env.d.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 59b3cb670..fd1793dfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `actor.oldGlobalPos` returns the globalPosition from the previous frame - Built in actions now have a unique `id` property +- create development builds of excalibur that bundlers can use in dev mode +- show warning in development when Entity hasn't been added to a scene after a few seconds ### Fixed diff --git a/karma.conf.js b/karma.conf.js index 1756de2ea..716ecf677 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -104,7 +104,8 @@ module.exports = (config) => { }, plugins: [ new webpack.DefinePlugin({ - 'process.env.__EX_VERSION': '\'test-runner\'' + 'process.env.__EX_VERSION': '\'test-runner\'', + 'process.env.NODE_ENV': JSON.stringify('test') }), ], module: { diff --git a/package.json b/package.json index 4e6765d82..81e61a7fd 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,22 @@ "homepage": "https://github.com/excaliburjs/Excalibur", "main": "build/dist/excalibur.min.js", "typings": "build/dist/excalibur.d.ts", - "module": "build/esm/excalibur.js", + "exports": { + "./package.json": "./package.json", + ".": { + "types": "./build/dist/excalibur.d.ts", + "development": { + "import": "./build/esm/excalibur.development.js", + "require": "./build/dist/excalibur.development.js" + }, + "production": { + "import": "./build/esm/excalibur.js", + "require": "./build/dist/excalibur.js" + }, + "import": "./build/esm/excalibur.js", + "require": "./build/dist/excalibur.js" + } + }, "repository": { "type": "git", "url": "git://github.com/excaliburjs/Excalibur.git" @@ -36,12 +51,14 @@ "start": "npm run core:watch", "start:esm": "npm run core:bundle:esm -- --watch", "build": "npm run core", - "build:esm": "npm run core:bundle:esm", - "core": "npm run core:tsc && npm run core:copy && npm run core:bundle", - "core:tsc": "tsc --project src/engine/tsconfig.json --incremental true --tsBuildInfoFile .tsbuildinfo && node ./scripts/excalibur-version.js", + "build:esm": "npm run core:bundle:esm && npm run core:bundle:esm:development", + "core": "npm run core:tsc && npm run core:copy && npm run core:bundle && npm run core:bundle:development", + "core:tsc": "tsc --project src/engine/tsconfig.json --incremental true --tsBuildInfoFile .tsbuildinfo --emitDeclarationOnly && node ./scripts/excalibur-version.js", "core:copy": "copyfiles -u 2 ./src/engine/**/*.png ./src/engine/**/*.css ./src/engine/**/*.glsl ./build/dist/", "core:bundle": "webpack --progress --config webpack.config.js --mode production", + "core:bundle:development": "webpack --progress --config webpack.config.js --mode development", "core:bundle:esm": "webpack --progress --config webpack.config.js --mode production --env output=esm", + "core:bundle:esm:development": "webpack --progress --config webpack.config.js --mode development --env output=esm", "core:watch": "npm run core:bundle -- --watch", "format": "prettier . --check", "format:fix": "npm run format -- --write", diff --git a/src/engine/EntityComponentSystem/Entity.ts b/src/engine/EntityComponentSystem/Entity.ts index 4def52306..a2f386148 100644 --- a/src/engine/EntityComponentSystem/Entity.ts +++ b/src/engine/EntityComponentSystem/Entity.ts @@ -9,6 +9,7 @@ import { EventEmitter, EventKey, Handler, Subscription } from '../EventEmitter'; import { Scene } from '../Scene'; import { removeItemFromArray } from '../Util/Util'; import { MaybeKnownComponent } from './Types'; +import { Logger } from '../Util/Log'; /** * Interface holding an entity component pair @@ -133,7 +134,14 @@ export class Entity implements OnIniti this.addComponent(component); } } - // this.addComponent(this.tagsComponent); + + if (process.env.NODE_ENV === 'development') { + setTimeout(() => { + if (!this.scene && !this.isInitialized) { + Logger.getInstance().warn(`Entity "${this.name || this.id}" was not added to a scene.`); + } + }, 5000); + } } /** diff --git a/src/engine/env.d.ts b/src/engine/env.d.ts new file mode 100644 index 000000000..d30b8b6e0 --- /dev/null +++ b/src/engine/env.d.ts @@ -0,0 +1,11 @@ +declare global { + namespace NodeJS { + interface ProcessEnv { + __EX_VERSION: string; + NODE_ENV: string; + } + } +} + +// satisfy TypeScript +export {}; diff --git a/src/engine/globals.d.ts b/src/engine/globals.d.ts index 8f14d4616..fa3d92c48 100644 --- a/src/engine/globals.d.ts +++ b/src/engine/globals.d.ts @@ -12,9 +12,3 @@ interface WheelEvent { readonly wheelDeltaY: number; readonly wheelDelta: number; } - -declare const process: { - env: { - __EX_VERSION: string; - }; -}; diff --git a/src/engine/tsconfig.json b/src/engine/tsconfig.json index 5610f236b..52fafbf7a 100644 --- a/src/engine/tsconfig.json +++ b/src/engine/tsconfig.json @@ -19,6 +19,7 @@ "allowUnreachableCode": false, "downlevelIteration": true, "lib": ["dom", "dom.iterable", "es5", "es2018"], - "types": [] - } + "types": ["node"] + }, + "include": ["./**/*.ts"] } diff --git a/webpack.config.js b/webpack.config.js index 258da3a1a..b3101024e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -7,28 +7,33 @@ const TerserPlugin = require('terser-webpack-plugin'); const now = new Date(); const dt = now.getFullYear() + '-' + (now.getMonth() + 1) + '-' + now.getDate(); -const umdOutput = { - path: path.resolve(__dirname, 'build/dist'), - filename: '[name].js', - library: { - name: 'ex', - type: 'umd' - } -}; - -const esmOutput = { - path: path.resolve(__dirname, 'build/esm'), - filename: '[name].js', - library: { - type: 'module' - } -}; - +/** + * @returns {import('webpack').Configuration} + */ module.exports = (env, argv) => { + const { mode } = argv; const version = process.env.release ? versioner.getReleaseVersion() : versioner.getAlphaVersion(); console.log('[version]:', version); + + const umdOutput = { + path: path.resolve(__dirname, 'build/dist'), + filename: mode === 'development' ? '[name].development.js' : '[name].js', + library: { + name: 'ex', + type: 'umd' + } + }; + + const esmOutput = { + path: path.resolve(__dirname, 'build/esm'), + filename: mode === 'development' ? '[name].development.js' : '[name].js', + library: { + type: 'module' + } + }; + return { - mode: 'production', + mode, devtool: 'source-map', entry: { excalibur: './index.ts', @@ -87,10 +92,12 @@ module.exports = (env, argv) => { } ] }, + plugins: [ new CopyWebpackPlugin({ patterns: ['excalibur.d.ts'] }), new webpack.DefinePlugin({ - 'process.env.__EX_VERSION': JSON.stringify(version) + 'process.env.__EX_VERSION': JSON.stringify(version), + 'process.env.NODE_ENV': JSON.stringify(mode) }), new webpack.BannerPlugin( `${pkg.name} - ${version} - ${dt}