xcratch-create 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create.js +2 -1
- package/package.json +3 -3
- package/template/.github/workflows/deploy.yml +54 -0
- package/template/.nvmrc +1 -0
- package/template/README.md +60 -0
- package/template/babel.config.json +16 -1
- package/template/package.json +20 -17
- package/template/scripts/.eslintrc.js +3 -0
- package/template/scripts/rollup.config.mjs +22 -5
- package/template/scripts/setup-dev.mjs +7 -1
- package/template/scripts/version-build.mjs +112 -0
- package/template/src/gui/lib/libraries/extensions/entry/index.jsx +5 -2
package/bin/create.js
CHANGED
|
@@ -4,7 +4,8 @@ import path from 'path';
|
|
|
4
4
|
import { replaceInFile } from 'replace-in-file';
|
|
5
5
|
import fs from 'fs-extra';
|
|
6
6
|
import AdmZip from 'adm-zip';
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
const projectJson = await fs.readJson(path.resolve(path.dirname(new URL(import.meta.url).pathname), '../package.json'));
|
|
8
9
|
|
|
9
10
|
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
|
10
11
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xcratch-create",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Support tool to create new extension for Xcratch",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"homepage": "https://github.com/xcratch/xcratch-create#readme",
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"adm-zip": "^0.5.16",
|
|
32
|
-
"fs-extra": "^11.3.
|
|
33
|
-
"replace-in-file": "^8.
|
|
32
|
+
"fs-extra": "^11.3.3",
|
|
33
|
+
"replace-in-file": "^8.4.0"
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Deploy
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
tags:
|
|
5
|
+
- 'v*.*.*'
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
branch:
|
|
9
|
+
description: "Branch to publish"
|
|
10
|
+
default: "main"
|
|
11
|
+
required: true
|
|
12
|
+
publish-dir:
|
|
13
|
+
description: "Publish directory"
|
|
14
|
+
type: string
|
|
15
|
+
default: "."
|
|
16
|
+
required: true
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
build-deploy:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
permissions:
|
|
22
|
+
contents: write
|
|
23
|
+
steps:
|
|
24
|
+
- name: inputs
|
|
25
|
+
run: echo "${{ toJSON(inputs) }}"
|
|
26
|
+
- name: Set branch and publish-dir
|
|
27
|
+
id: config
|
|
28
|
+
run: |
|
|
29
|
+
if [ "${{ github.event_name }}" = "push" ]; then
|
|
30
|
+
echo "branch=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
|
31
|
+
echo "publish-dir=." >> $GITHUB_OUTPUT
|
|
32
|
+
else
|
|
33
|
+
echo "branch=${{ inputs.branch }}" >> $GITHUB_OUTPUT
|
|
34
|
+
echo "publish-dir=${{ inputs.publish-dir }}" >> $GITHUB_OUTPUT
|
|
35
|
+
fi
|
|
36
|
+
- name: checkout repository
|
|
37
|
+
uses: actions/checkout@v4
|
|
38
|
+
with:
|
|
39
|
+
ref: ${{ steps.config.outputs.branch }}
|
|
40
|
+
path: ./repo
|
|
41
|
+
- name: copy files
|
|
42
|
+
run: |
|
|
43
|
+
mkdir -p ./publish
|
|
44
|
+
cp -r ./repo/dist ./publish/dist
|
|
45
|
+
cp -r ./repo/projects ./publish/projects
|
|
46
|
+
cp -r ./repo/README.md ./publish/README.md
|
|
47
|
+
- name: publish to GitHub Pages
|
|
48
|
+
uses: peaceiris/actions-gh-pages@v4
|
|
49
|
+
with:
|
|
50
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
51
|
+
publish_dir: ./publish
|
|
52
|
+
destination_dir: ${{ steps.config.outputs.publish-dir }}
|
|
53
|
+
keep_files: false
|
|
54
|
+
enable_jekyll: true
|
package/template/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
20.19.4
|
package/template/README.md
CHANGED
|
@@ -64,6 +64,66 @@ Run test script to test this extension.
|
|
|
64
64
|
npm run test
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
### Versioning and Deployment
|
|
68
|
+
|
|
69
|
+
This project uses npm version commands and GitHub Actions for versioning and deployment.
|
|
70
|
+
|
|
71
|
+
#### Create a New Version
|
|
72
|
+
|
|
73
|
+
Use npm version command to update the version number. This will automatically:
|
|
74
|
+
1. Update version in `package.json`
|
|
75
|
+
2. Run the build script
|
|
76
|
+
3. Create version-specific build files in `dist/{version}/`
|
|
77
|
+
4. Update `dist/versions.json` with the new version info
|
|
78
|
+
5. Create a git commit and tag
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
# Patch version (1.3.0 → 1.3.1)
|
|
82
|
+
npm version patch
|
|
83
|
+
|
|
84
|
+
# Minor version (1.3.1 → 1.4.0)
|
|
85
|
+
npm version minor
|
|
86
|
+
|
|
87
|
+
# Major version (1.4.0 → 2.0.0)
|
|
88
|
+
npm version major
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### Deploy to GitHub Pages
|
|
92
|
+
|
|
93
|
+
After creating a new version, push the tag to trigger automatic deployment:
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
# Push the version tag
|
|
97
|
+
git push origin v1.4.0
|
|
98
|
+
|
|
99
|
+
# Or push all tags
|
|
100
|
+
git push --tags
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The GitHub Actions workflow will:
|
|
104
|
+
1. Build the extension
|
|
105
|
+
2. Deploy `dist/`, `projects/`, and `README.md` to GitHub Pages
|
|
106
|
+
|
|
107
|
+
You can also manually trigger deployment from the Actions tab in GitHub.
|
|
108
|
+
|
|
109
|
+
#### Version Information
|
|
110
|
+
|
|
111
|
+
All build versions are recorded in `dist/versions.json`:
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"extensionId": "<<extensionID>>",
|
|
116
|
+
"latest": "1.0.0",
|
|
117
|
+
"versions": [
|
|
118
|
+
{
|
|
119
|
+
"version": "1.0.0",
|
|
120
|
+
"buildDate": "2025-10-19T12:34:56.789Z",
|
|
121
|
+
"module": "1.0.0/<<extensionID>>.mjs"
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
67
127
|
|
|
68
128
|
## 🏠 Home Page
|
|
69
129
|
|
package/template/package.json
CHANGED
|
@@ -2,35 +2,38 @@
|
|
|
2
2
|
"name": "<<repo>>",
|
|
3
3
|
"version": "0.0.0",
|
|
4
4
|
"description": "extension for Xcratch",
|
|
5
|
+
"extensionId": "<<extensionID>>",
|
|
5
6
|
"scripts": {
|
|
6
7
|
"setup-dev": "node ./scripts/setup-dev.mjs",
|
|
7
8
|
"build": "rollup -c ./scripts/rollup.config.mjs",
|
|
8
9
|
"watch": "rollup -c ./scripts/rollup.config.mjs --watch",
|
|
9
|
-
"test": "
|
|
10
|
+
"test": "jest",
|
|
11
|
+
"version": "node ./scripts/version-build.mjs && git add dist"
|
|
10
12
|
},
|
|
11
13
|
"devDependencies": {
|
|
12
|
-
"@babel/core": "^7.
|
|
13
|
-
"@babel/eslint-parser": "^7.
|
|
14
|
-
"@babel/plugin-transform-react-jsx": "^7.
|
|
15
|
-
"@babel/plugin-transform-runtime": "^7.
|
|
16
|
-
"@babel/preset-env": "^7.
|
|
17
|
-
"@babel/preset-react": "^7.
|
|
18
|
-
"@babel/register": "^7.
|
|
14
|
+
"@babel/core": "^7.28.0",
|
|
15
|
+
"@babel/eslint-parser": "^7.28.0",
|
|
16
|
+
"@babel/plugin-transform-react-jsx": "^7.27.1",
|
|
17
|
+
"@babel/plugin-transform-runtime": "^7.28.0",
|
|
18
|
+
"@babel/preset-env": "^7.28.0",
|
|
19
|
+
"@babel/preset-react": "^7.27.1",
|
|
20
|
+
"@babel/register": "^7.27.1",
|
|
21
|
+
"@babel/runtime": "^7.28.2",
|
|
19
22
|
"@rollup/plugin-babel": "^6.0.4",
|
|
20
|
-
"@rollup/plugin-commonjs": "^28.0.
|
|
23
|
+
"@rollup/plugin-commonjs": "^28.0.6",
|
|
21
24
|
"@rollup/plugin-image": "^3.0.3",
|
|
22
25
|
"@rollup/plugin-json": "^6.1.0",
|
|
23
26
|
"@rollup/plugin-multi-entry": "^6.0.1",
|
|
24
|
-
"@rollup/plugin-node-resolve": "^16.0.
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
+
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
28
|
+
"babel-jest": "^30.0.5",
|
|
29
|
+
"core-js": "^3.44.0",
|
|
30
|
+
"eslint": "^8.57.1",
|
|
27
31
|
"eslint-config-scratch": "^9.0.9",
|
|
28
|
-
"eslint-plugin-import": "^2.
|
|
29
|
-
"eslint-plugin-jest": "^
|
|
32
|
+
"eslint-plugin-import": "^2.32.0",
|
|
33
|
+
"eslint-plugin-jest": "^29.0.1",
|
|
30
34
|
"fs-extra": "^11.3.0",
|
|
31
|
-
"jest": "^
|
|
32
|
-
"
|
|
33
|
-
"rollup": "^4.34.7",
|
|
35
|
+
"jest": "^30.0.5",
|
|
36
|
+
"rollup": "^4.46.1",
|
|
34
37
|
"rollup-plugin-polyfill-node": "^0.13.0"
|
|
35
38
|
}
|
|
36
39
|
}
|
|
@@ -9,6 +9,15 @@ import importImage from '@rollup/plugin-image';
|
|
|
9
9
|
import multi from '@rollup/plugin-multi-entry';
|
|
10
10
|
import json from '@rollup/plugin-json';
|
|
11
11
|
|
|
12
|
+
// Read package.json to get extensionId
|
|
13
|
+
const packageJsonPath = path.resolve(process.cwd(), './package.json');
|
|
14
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
15
|
+
const EXTENSION_ID = packageJson.extensionId;
|
|
16
|
+
if (!EXTENSION_ID) {
|
|
17
|
+
console.error('Error: extensionId not found in package.json');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
// path for block
|
|
13
22
|
const blockSrcDir = path.resolve(process.cwd(), './src/vm/extensions/block');
|
|
14
23
|
const blockFile = path.resolve(blockSrcDir, 'index.js');
|
|
@@ -16,13 +25,12 @@ const blockFile = path.resolve(blockSrcDir, 'index.js');
|
|
|
16
25
|
const entrySrcDir = path.resolve(process.cwd(), './src/gui/lib/libraries/extensions/entry');
|
|
17
26
|
const entryFile = path.resolve(entrySrcDir, 'index.jsx');
|
|
18
27
|
// path for output
|
|
19
|
-
const moduleName = '<<extensionID>>';
|
|
20
28
|
const outputDir = path.resolve(process.cwd(), './dist');
|
|
21
|
-
|
|
22
|
-
const moduleFile = path.resolve(outputDir, `${moduleName}.mjs`);
|
|
29
|
+
const moduleFile = path.resolve(outputDir, `${EXTENSION_ID}.mjs`);
|
|
23
30
|
|
|
24
31
|
const rollupOptions = {
|
|
25
32
|
input: [entryFile, blockFile],
|
|
33
|
+
context: `window`,
|
|
26
34
|
plugins: [
|
|
27
35
|
multi(),
|
|
28
36
|
importImage(),
|
|
@@ -34,10 +42,14 @@ const rollupOptions = {
|
|
|
34
42
|
modulePaths: [
|
|
35
43
|
path.resolve(process.cwd(), './node_modules'),
|
|
36
44
|
],
|
|
45
|
+
// Add these options to better resolve @babel/runtime
|
|
46
|
+
include: ['**'],
|
|
47
|
+
skip: [],
|
|
37
48
|
}),
|
|
38
49
|
json(),
|
|
39
50
|
babel({
|
|
40
51
|
babelrc: false,
|
|
52
|
+
exclude: ['node_modules/**'],
|
|
41
53
|
presets: [
|
|
42
54
|
['@babel/preset-env',
|
|
43
55
|
{
|
|
@@ -55,8 +67,13 @@ const rollupOptions = {
|
|
|
55
67
|
babelHelpers: 'runtime',
|
|
56
68
|
plugins: [
|
|
57
69
|
'@babel/plugin-transform-react-jsx',
|
|
58
|
-
[
|
|
59
|
-
|
|
70
|
+
[
|
|
71
|
+
"@babel/plugin-transform-runtime",
|
|
72
|
+
{
|
|
73
|
+
"regenerator": true,
|
|
74
|
+
"useESModules": true
|
|
75
|
+
}
|
|
76
|
+
]
|
|
60
77
|
],
|
|
61
78
|
}),
|
|
62
79
|
],
|
|
@@ -4,14 +4,20 @@
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import fs from 'fs-extra';
|
|
6
6
|
|
|
7
|
+
// Get scratch-vm path from command line argument
|
|
8
|
+
const args = process.argv.slice(2);
|
|
9
|
+
const vmPath = args[0] || '../scratch-editor/packages/scratch-vm';
|
|
10
|
+
|
|
7
11
|
// modify for your environment
|
|
8
12
|
const vmSrcDev = path.resolve(process.cwd(), './src/vm');
|
|
9
|
-
const vmSrcOrg = path.resolve(process.cwd(), '
|
|
13
|
+
const vmSrcOrg = path.resolve(process.cwd(), vmPath, 'src');
|
|
10
14
|
const vmRefs = [
|
|
11
15
|
'extension-support',
|
|
12
16
|
'util',
|
|
13
17
|
];
|
|
14
18
|
|
|
19
|
+
console.log(`Using scratch-vm at: ${vmSrcOrg}`);
|
|
20
|
+
|
|
15
21
|
// Make symbolic link
|
|
16
22
|
const makeSymbolicLink = function (to, from) {
|
|
17
23
|
try {
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
|
|
9
|
+
const projectRoot = path.resolve(__dirname, '..');
|
|
10
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
11
|
+
const distDir = path.join(projectRoot, 'dist');
|
|
12
|
+
|
|
13
|
+
// Read package.json to get version and extensionId
|
|
14
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
15
|
+
const version = packageJson.version;
|
|
16
|
+
const EXTENSION_ID = packageJson.extensionId;
|
|
17
|
+
if (!EXTENSION_ID) {
|
|
18
|
+
console.error('Error: extensionId not found in package.json');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
console.log(`Building ${EXTENSION_ID} version ${version}...`);
|
|
23
|
+
|
|
24
|
+
// Run rollup build
|
|
25
|
+
try {
|
|
26
|
+
execSync('npm run build', {
|
|
27
|
+
cwd: projectRoot,
|
|
28
|
+
stdio: 'inherit'
|
|
29
|
+
});
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error('Build failed:', error.message);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Create version directory and copy files
|
|
36
|
+
const sourceFile = path.join(distDir, `${EXTENSION_ID}.mjs`);
|
|
37
|
+
const versionDir = path.join(distDir, version);
|
|
38
|
+
const targetFile = path.join(versionDir, `${EXTENSION_ID}.mjs`);
|
|
39
|
+
|
|
40
|
+
if (fs.existsSync(sourceFile)) {
|
|
41
|
+
// Create version directory
|
|
42
|
+
fs.ensureDirSync(versionDir);
|
|
43
|
+
|
|
44
|
+
// Copy the module file
|
|
45
|
+
fs.copyFileSync(sourceFile, targetFile);
|
|
46
|
+
console.log(`✓ Created: dist/${version}/${EXTENSION_ID}.mjs`);
|
|
47
|
+
|
|
48
|
+
// Also copy sourcemap if it exists
|
|
49
|
+
const sourceMapFile = `${sourceFile}.map`;
|
|
50
|
+
const targetMapFile = `${targetFile}.map`;
|
|
51
|
+
if (fs.existsSync(sourceMapFile)) {
|
|
52
|
+
fs.copyFileSync(sourceMapFile, targetMapFile);
|
|
53
|
+
console.log(`✓ Created: dist/${version}/${EXTENSION_ID}.mjs.map`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Update versions.json file
|
|
57
|
+
const versionJsonPath = path.join(distDir, 'versions.json');
|
|
58
|
+
let versionData = {
|
|
59
|
+
extensionId: EXTENSION_ID,
|
|
60
|
+
versions: []
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Read existing versions.json if it exists
|
|
64
|
+
if (fs.existsSync(versionJsonPath)) {
|
|
65
|
+
try {
|
|
66
|
+
versionData = JSON.parse(fs.readFileSync(versionJsonPath, 'utf-8'));
|
|
67
|
+
// Ensure versions array exists
|
|
68
|
+
if (!versionData.versions) {
|
|
69
|
+
versionData.versions = [];
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.warn('Warning: Could not parse existing versions.json, creating new one');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Remove existing entry for this version if it exists
|
|
77
|
+
versionData.versions = versionData.versions.filter(v => v.version !== version);
|
|
78
|
+
|
|
79
|
+
// Add new version info
|
|
80
|
+
const newVersionInfo = {
|
|
81
|
+
version: version,
|
|
82
|
+
buildDate: new Date().toISOString(),
|
|
83
|
+
module: `${version}/${EXTENSION_ID}.mjs`
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
versionData.versions.push(newVersionInfo);
|
|
87
|
+
|
|
88
|
+
// Sort versions in descending order (newest first)
|
|
89
|
+
versionData.versions.sort((a, b) => {
|
|
90
|
+
const versionA = a.version.split('.').map(Number);
|
|
91
|
+
const versionB = b.version.split('.').map(Number);
|
|
92
|
+
for (let i = 0; i < Math.max(versionA.length, versionB.length); i++) {
|
|
93
|
+
const numA = versionA[i] || 0;
|
|
94
|
+
const numB = versionB[i] || 0;
|
|
95
|
+
if (numA !== numB) return numB - numA;
|
|
96
|
+
}
|
|
97
|
+
return 0;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Add latest version reference
|
|
101
|
+
versionData.latest = version;
|
|
102
|
+
|
|
103
|
+
// Write versions.json
|
|
104
|
+
fs.writeFileSync(versionJsonPath, JSON.stringify(versionData, null, 2), 'utf-8');
|
|
105
|
+
console.log(`✓ Updated: dist/versions.json`);
|
|
106
|
+
|
|
107
|
+
} else {
|
|
108
|
+
console.error(`Error: ${sourceFile} not found`);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log(`\n✓ Version ${version} build completed successfully!`);
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import iconURL from './entry-icon.png';
|
|
6
6
|
import insetIconURL from './inset-icon.svg';
|
|
7
7
|
import translations from './translations.json';
|
|
8
|
+
import {version as packageVersion} from '../../../../../../package.json';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Formatter to translate the messages in this extension.
|
|
@@ -14,6 +15,8 @@ import translations from './translations.json';
|
|
|
14
15
|
*/
|
|
15
16
|
let formatMessage = messageData => messageData.defaultMessage;
|
|
16
17
|
|
|
18
|
+
const version = `v${packageVersion}`;
|
|
19
|
+
|
|
17
20
|
const entry = {
|
|
18
21
|
get name () {
|
|
19
22
|
return formatMessage({
|
|
@@ -28,11 +31,11 @@ const entry = {
|
|
|
28
31
|
iconURL: iconURL,
|
|
29
32
|
insetIconURL: insetIconURL,
|
|
30
33
|
get description () {
|
|
31
|
-
return formatMessage({
|
|
34
|
+
return `${formatMessage({
|
|
32
35
|
defaultMessage: 'an extension for Xcratch',
|
|
33
36
|
description: 'Description for this extension',
|
|
34
37
|
id: '<<extensionID>>.entry.description'
|
|
35
|
-
})
|
|
38
|
+
})} (${version})`;
|
|
36
39
|
},
|
|
37
40
|
tags: [],
|
|
38
41
|
featured: true,
|