build!: use scratch-webpack-configuration

BREAKING CHANGE: changes layout of output files and browser compatibility
This commit is contained in:
Christopher Willis-Ford 2024-03-13 11:09:44 -07:00
parent d963343d40
commit f1438f73e3
9 changed files with 211 additions and 368 deletions

View File

@ -7,7 +7,7 @@
"messagesDir": "./translations/messages/"
}]],
"presets": [
["@babel/preset-env", {"targets": {"browsers": ["last 3 versions", "Safari >= 8", "iOS >= 8"]}}],
"@babel/preset-env",
"@babel/preset-react"
]
}

View File

@ -1,3 +1,7 @@
last 3 versions
Safari >= 8
iOS >= 8
# See https://scratch.mit.edu/faq
Chrome >= 63
Edge >= 15
Firefox >= 57
Safari >= 11
Android >= 63
iOS >= 11

View File

@ -1,5 +1,4 @@
node_modules/*
build/*
dist/*
test/*
src/examples/*

146
package-lock.json generated
View File

@ -109,9 +109,9 @@
"redux-mock-store": "1.5.4",
"rimraf": "2.7.1",
"scratch-semantic-release-config": "1.0.14",
"scratch-webpack-configuration": "1.1.0",
"selenium-webdriver": "3.6.0",
"semantic-release": "19.0.5",
"terser-webpack-plugin": "4.2.3",
"url-loader": "4.1.1",
"web-audio-test-api": "0.5.2",
"webpack": "5.90.3",
@ -15211,41 +15211,6 @@
"pretty-format": "^21.2.1"
}
},
"node_modules/jest-worker": {
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
"integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
"dev": true,
"dependencies": {
"@types/node": "*",
"merge-stream": "^2.0.0",
"supports-color": "^7.0.0"
},
"engines": {
"node": ">= 10.13.0"
}
},
"node_modules/jest-worker/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/jest-worker/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/js-base64": {
"version": "2.4.9",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.9.tgz",
@ -23171,6 +23136,21 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/scratch-webpack-configuration": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/scratch-webpack-configuration/-/scratch-webpack-configuration-1.1.0.tgz",
"integrity": "sha512-YjdOWGphdgOa/0HA3D6f2+WVLd0rj5UuwqowyLMZsXDGeoqlOHz2RVObWCkZ+/HBcQxsyy7gMjPX3dV6Mj+rbQ==",
"dev": true,
"dependencies": {
"lodash.merge": "^4.6.2",
"webpack-node-externals": "^3.0.0"
},
"peerDependencies": {
"@babel/preset-env": "^7.24.0",
"babel-loader": "^9.1.3",
"webpack": "^5.90.3"
}
},
"node_modules/seek-bzip": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz",
@ -25093,91 +25073,6 @@
"node": ">=10"
}
},
"node_modules/terser-webpack-plugin": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz",
"integrity": "sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==",
"dev": true,
"dependencies": {
"cacache": "^15.0.5",
"find-cache-dir": "^3.3.1",
"jest-worker": "^26.5.0",
"p-limit": "^3.0.2",
"schema-utils": "^3.0.0",
"serialize-javascript": "^5.0.1",
"source-map": "^0.6.1",
"terser": "^5.3.4",
"webpack-sources": "^1.4.3"
},
"engines": {
"node": ">= 10.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
},
"peerDependencies": {
"webpack": "^4.0.0 || ^5.0.0"
}
},
"node_modules/terser-webpack-plugin/node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/terser-webpack-plugin/node_modules/ajv-keywords": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true,
"peerDependencies": {
"ajv": "^6.9.1"
}
},
"node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"node_modules/terser-webpack-plugin/node_modules/schema-utils": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
"integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
"dev": true,
"dependencies": {
"@types/json-schema": "^7.0.8",
"ajv": "^6.12.5",
"ajv-keywords": "^3.5.2"
},
"engines": {
"node": ">= 10.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/terser-webpack-plugin/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/terser/node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
@ -26998,6 +26893,15 @@
"node": ">=10.0.0"
}
},
"node_modules/webpack-node-externals": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz",
"integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/webpack-sources": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",

View File

@ -11,9 +11,9 @@
},
"main": "./dist/scratch-gui.js",
"scripts": {
"build": "npm run clean && webpack",
"clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist",
"deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"[skip ci] Build for $(git log --pretty=format:%H -n1)\"",
"build": "npm run clean && mkdirp dist && webpack",
"clean": "rimraf ./dist",
"deploy": "touch build/.nojekyll && gh-pages -t -d dist -m \"[skip ci] Build for $(git log --pretty=format:%H -n1)\"",
"prepublish": "node scripts/prepublish.mjs",
"prune": "./prune-gh-pages.sh",
"i18n:push": "tx-push-src scratch-editor interface translations/en.json",
@ -136,9 +136,9 @@
"redux-mock-store": "1.5.4",
"rimraf": "2.7.1",
"scratch-semantic-release-config": "1.0.14",
"scratch-webpack-configuration": "1.1.0",
"selenium-webdriver": "3.6.0",
"semantic-release": "19.0.5",
"terser-webpack-plugin": "4.2.3",
"url-loader": "4.1.1",
"web-audio-test-api": "0.5.2",
"webpack": "5.90.3",
@ -158,6 +158,9 @@
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/test/__mocks__/fileMock.js",
"\\.(css|less)$": "<rootDir>/test/__mocks__/styleMock.js",
"editor-msgs(\\.js)?$": "<rootDir>/test/__mocks__/editor-msgs-mock.js"
},
"transform": {
"^.+\\.[cm]?jsx?$": "babel-jest"
}
}
}

View File

@ -1,5 +1,5 @@
// eslint-disable-next-line import/no-unresolved
import soundThumbnail from '!base64-loader!./sound-thumbnail.jpg';
import soundThumbnail from '!base64-loader!./sound-thumbnail.jpg?';
const soundPayload = sound => {
const assetDataUrl = sound.asset.encodeDataURI();

View File

@ -1,11 +1,11 @@
import projectData from './project-data';
/* eslint-disable import/no-unresolved */
import popWav from '!arraybuffer-loader!./83a9787d4cb6f3b7632b4ddfebf74367.wav';
import meowWav from '!arraybuffer-loader!./83c36d806dc92327b9e7049a565c6bff.wav';
import backdrop from '!raw-loader!./cd21514d0531fdffb22204e0ec5ed84a.svg';
import costume1 from '!raw-loader!./bcf454acf82e4504149f7ffe07081dbc.svg';
import costume2 from '!raw-loader!./0fb9be3e8397c983338cb71dc84d0b25.svg';
import popWav from '!arraybuffer-loader!./83a9787d4cb6f3b7632b4ddfebf74367.wav?';
import meowWav from '!arraybuffer-loader!./83c36d806dc92327b9e7049a565c6bff.wav?';
import backdrop from '!raw-loader!./cd21514d0531fdffb22204e0ec5ed84a.svg?';
import costume1 from '!raw-loader!./bcf454acf82e4504149f7ffe07081dbc.svg?';
import costume2 from '!raw-loader!./0fb9be3e8397c983338cb71dc84d0b25.svg?';
/* eslint-enable import/no-unresolved */
const defaultProject = translator => {

View File

@ -1,7 +1,6 @@
// TODO: access `BlockType` and `ArgumentType` without reaching into VM
// Should we move these into a new extension support module or something?
import ArgumentType from 'scratch-vm/src/extension-support/argument-type';
import BlockType from 'scratch-vm/src/extension-support/block-type';
import {ArgumentType, BlockType} from 'scratch-vm';
/**
* Define a block using extension info which has the ability to dynamically determine (and update) its layout.

View File

@ -1,64 +1,75 @@
const defaultsDeep = require('lodash.defaultsdeep');
const path = require('path');
const webpack = require('webpack');
// Plugins
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
// PostCss
const autoprefixer = require('autoprefixer');
const postcssVars = require('postcss-simple-vars');
const postcssImport = require('postcss-import');
const STATIC_PATH = process.env.STATIC_PATH || '/static';
const ScratchWebpackConfigBuilder = require('scratch-webpack-configuration');
const base = {
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
devtool: 'cheap-module-source-map',
devServer: {
contentBase: path.resolve(__dirname, 'build'),
host: '0.0.0.0',
port: process.env.PORT || 8601
},
output: {
library: 'GUI',
filename: '[name].js',
chunkFilename: 'chunks/[name].js'
},
resolve: {
symlinks: false
},
module: {
rules: [{
test: /\.jsx?$/,
loader: 'babel-loader',
include: [
path.resolve(__dirname, 'src'),
/node_modules[\\/]scratch-[^\\/]+[\\/]src/,
/node_modules[\\/]pify/,
/node_modules[\\/]@vernier[\\/]godirect/
],
options: {
// Explicitly disable babelrc so we don't catch various config
// in much lower dependencies.
babelrc: false,
plugins: [
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-transform-async-to-generator',
'@babel/plugin-proposal-object-rest-spread',
['react-intl', {
messagesDir: './translations/messages/'
}]],
presets: ['@babel/preset-env', '@babel/preset-react']
// const STATIC_PATH = process.env.STATIC_PATH || '/static';
const configBuilder = new ScratchWebpackConfigBuilder(
{
rootPath: path.resolve(__dirname),
enableReact: true
})
.setTarget('browserslist')
.merge({
devServer: {
client: {
progress: true
},
hot: true,
port: process.env.PORT || 8602
},
entry: {
// GUI as a library
'scratch-gui': path.join(__dirname, 'src/index.js'),
// to run editor examples
'lib.min': ['react', 'react-dom'],
'gui': './src/playground/index.jsx',
'blocksonly': './src/playground/blocks-only.jsx',
'compatibilitytesting': './src/playground/compatibility-testing.jsx',
'player': './src/playground/player.jsx'
},
output: {
assetModuleFilename: 'static/assets/[name].[hash][ext][query]',
chunkFilename: 'chunks/[name].js',
library: {
name: 'GUI',
type: 'umd2'
},
// publicPath: `${STATIC_PATH}/`,
path: path.resolve(__dirname, 'dist')
},
resolve: {
fallback: {
Buffer: require.resolve('buffer/')
}
},
{
test: /\.css$/,
use: [{
optimization: {
splitChunks: {
chunks: 'all',
filename: 'chunks/[name].js'
},
mergeDuplicateChunks: true,
runtimeChunk: 'single'
}
})
.addModuleRule({
test: /\.css$/,
use: [
{
loader: 'style-loader'
}, {
},
{
loader: 'css-loader',
options: {
modules: {
@ -67,7 +78,8 @@ const base = {
importLoaders: 1,
localsConvention: 'camelCase'
}
}, {
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
@ -79,192 +91,114 @@ const base = {
];
}
}
}]
},
{
test: /\.hex$/,
use: [{
loader: 'url-loader',
options: {
limit: 16 * 1024
}
}]
}]
},
optimization: {
minimizer: [
new TerserPlugin({
include: /\.min\.js$/
})
}
]
},
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: 'node_modules/scratch-blocks/media',
to: 'static/blocks-media/default'
},
{
from: 'node_modules/scratch-blocks/media',
to: 'static/blocks-media/high-contrast'
},
{
from: 'src/lib/themes/high-contrast/blocks-media',
to: 'static/blocks-media/high-contrast',
force: true
}
]
})
]
};
})
.addModuleRule({
test: /\.(svg|png|wav|mp3|gif|jpg)$/,
resourceQuery: /^$/, // reject any query string
type: 'asset' // let webpack decide on the best type of asset
})
.addModuleRule({
// `asset` automatically chooses between exporting a data URI and emitting a separate file.
// Previously achievable by using `url-loader` with asset size limit.
resourceQuery: /^\?asset$/,
type: 'asset'
})
.addModuleRule({
// `asset/resource` emits a separate file and exports the URL.
// Previously achievable by using `file-loader`.
resourceQuery: /^\?(resource|file)$/,
type: 'asset/resource'
})
.addModuleRule({
// `asset/inline` exports a data URI of the asset.
// Previously achievable by using `url-loader`.
resourceQuery: /^\?(inline|url)$/,
type: 'asset/inline'
})
.addModuleRule({
// `asset/source` exports the source code of the asset.
// Previously achievable by using `raw-loader`.
resourceQuery: /^\?(source|raw)$/,
type: 'asset/source'
})
.addModuleRule({
test: /\.hex$/,
type: 'asset/resource'
})
.addPlugin(new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer']
}))
.addPlugin(new webpack.DefinePlugin({
'process.env.DEBUG': Boolean(process.env.DEBUG),
'process.env.GA_ID': `"${process.env.GA_ID || 'UA-000000-01'}"`,
'process.env.GTM_ENV_AUTH': `"${process.env.GTM_ENV_AUTH || ''}"`,
'process.env.GTM_ID': process.env.GTM_ID ? `"${process.env.GTM_ID}"` : null
}))
.addPlugin(new HtmlWebpackPlugin({
chunks: ['gui'],
template: 'src/playground/index.ejs',
title: 'Scratch 3.0 GUI'
}))
.addPlugin(new HtmlWebpackPlugin({
chunks: ['blocksonly'],
filename: 'blocks-only.html',
template: 'src/playground/index.ejs',
title: 'Scratch 3.0 GUI: Blocks Only Example'
}))
.addPlugin(new HtmlWebpackPlugin({
chunks: ['compatibilitytesting'],
filename: 'compatibility-testing.html',
template: 'src/playground/index.ejs',
title: 'Scratch 3.0 GUI: Compatibility Testing'
}))
.addPlugin(new HtmlWebpackPlugin({
chunks: ['player'],
filename: 'player.html',
template: 'src/playground/index.ejs',
title: 'Scratch 3.0 GUI: Player Example'
}))
.addPlugin(new CopyWebpackPlugin({
patterns: [
{
from: 'static',
to: 'static'
},
{
from: 'extensions/**',
to: 'static',
context: 'src/examples'
},
{
from: 'src/lib/themes/high-contrast/blocks-media',
to: 'static/blocks-media/high-contrast',
force: true
},
{
// Include library JSON files for scratch-desktop to use for downloading
from: 'src/lib/libraries/*.json',
to: 'libraries',
flatten: true
},
{
from: 'node_modules/scratch-blocks/media',
to: 'static/blocks-media/default'
},
{
from: 'node_modules/scratch-blocks/media',
to: 'static/blocks-media/high-contrast'
},
{
context: 'node_modules/scratch-vm/dist/web',
from: 'extension-worker.{js,js.map}',
noErrorOnMissing: true
}
]
}));
if (!process.env.CI) {
base.plugins.push(new webpack.ProgressPlugin());
configBuilder.addPlugin(new webpack.ProgressPlugin());
}
module.exports = [
// to run editor examples
defaultsDeep({}, base, {
entry: {
'lib.min': ['react', 'react-dom'],
'gui': './src/playground/index.jsx',
'blocksonly': './src/playground/blocks-only.jsx',
'compatibilitytesting': './src/playground/compatibility-testing.jsx',
'player': './src/playground/player.jsx'
},
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].js'
},
module: {
rules: base.module.rules.concat([
{
test: /\.(svg|png|wav|mp3|gif|jpg)$/,
loader: 'url-loader',
options: {
limit: 2048,
outputPath: 'static/assets/'
}
}
])
},
optimization: {
splitChunks: {
chunks: 'all',
name: 'lib.min'
},
runtimeChunk: {
name: 'lib.min'
}
},
plugins: base.plugins.concat([
new webpack.DefinePlugin({
'process.env.NODE_ENV': `"${process.env.NODE_ENV}"`,
'process.env.DEBUG': Boolean(process.env.DEBUG),
'process.env.GA_ID': `"${process.env.GA_ID || 'UA-000000-01'}"`
}),
new HtmlWebpackPlugin({
chunks: ['lib.min', 'gui'],
template: 'src/playground/index.ejs',
title: 'Scratch 3.0 GUI'
}),
new HtmlWebpackPlugin({
chunks: ['lib.min', 'blocksonly'],
template: 'src/playground/index.ejs',
filename: 'blocks-only.html',
title: 'Scratch 3.0 GUI: Blocks Only Example'
}),
new HtmlWebpackPlugin({
chunks: ['lib.min', 'compatibilitytesting'],
template: 'src/playground/index.ejs',
filename: 'compatibility-testing.html',
title: 'Scratch 3.0 GUI: Compatibility Testing'
}),
new HtmlWebpackPlugin({
chunks: ['lib.min', 'player'],
template: 'src/playground/index.ejs',
filename: 'player.html',
title: 'Scratch 3.0 GUI: Player Example'
}),
new CopyWebpackPlugin({
patterns: [
{
from: 'static',
to: 'static'
}
]
}),
new CopyWebpackPlugin({
patterns: [
{
from: 'extensions/**',
to: 'static',
context: 'src/examples'
}
]
}),
new CopyWebpackPlugin({
patterns: [
{
from: 'extension-worker.{js,js.map}',
context: 'node_modules/scratch-vm/dist/web',
noErrorOnMissing: true
}
]
})
])
})
].concat(
process.env.NODE_ENV === 'production' || process.env.BUILD_MODE === 'dist' ? (
// export as library
defaultsDeep({}, base, {
target: 'web',
entry: {
'scratch-gui': './src/index.js'
},
output: {
libraryTarget: 'umd',
path: path.resolve('dist'),
publicPath: `${STATIC_PATH}/`
},
externals: {
'react': 'react',
'react-dom': 'react-dom'
},
module: {
rules: base.module.rules.concat([
{
test: /\.(svg|png|wav|mp3|gif|jpg)$/,
loader: 'url-loader',
options: {
limit: 2048,
outputPath: 'static/assets/',
publicPath: `${STATIC_PATH}/assets/`
}
}
])
},
plugins: base.plugins.concat([
new CopyWebpackPlugin({
patterns: [
{
from: 'extension-worker.{js,js.map}',
context: 'node_modules/scratch-vm/dist/web',
noErrorOnMissing: true
}
]
}),
// Include library JSON files for scratch-desktop to use for downloading
new CopyWebpackPlugin({
patterns: [
{
from: 'src/lib/libraries/*.json',
to: 'libraries',
flatten: true
}
]
})
])
})) : []
);
module.exports = configBuilder.get();