xcratch-create 1.2.0 → 1.3.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 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
- import projectJson from '../package.json' assert { type: 'json' };
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.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Support tool to create new extension for Xcratch",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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
@@ -0,0 +1 @@
1
+ 20.19.4
@@ -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
 
@@ -14,5 +14,20 @@
14
14
  }
15
15
  ],
16
16
  "@babel/preset-react"
17
- ]
17
+ ],
18
+ "env": {
19
+ "test": {
20
+ "presets": [
21
+ [
22
+ "@babel/preset-env",
23
+ {
24
+ "targets": {
25
+ "node": "current"
26
+ },
27
+ "modules": "commonjs"
28
+ }
29
+ ]
30
+ ]
31
+ }
32
+ }
18
33
  }
@@ -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": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
10
+ "test": "jest",
11
+ "version": "node ./scripts/version-build.mjs && git add dist"
10
12
  },
11
13
  "devDependencies": {
12
- "@babel/core": "^7.26.9",
13
- "@babel/eslint-parser": "^7.26.8",
14
- "@babel/plugin-transform-react-jsx": "^7.25.9",
15
- "@babel/plugin-transform-runtime": "^7.26.9",
16
- "@babel/preset-env": "^7.26.9",
17
- "@babel/preset-react": "^7.26.3",
18
- "@babel/register": "^7.25.9",
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.2",
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.0",
25
- "core-js": "^3.40.0",
26
- "eslint": "^8.57.0",
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.31.0",
29
- "eslint-plugin-jest": "^27.6.3",
32
+ "eslint-plugin-import": "^2.32.0",
33
+ "eslint-plugin-jest": "^29.0.1",
30
34
  "fs-extra": "^11.3.0",
31
- "jest": "^29.7.0",
32
- "jest-environment-jsdom": "^29.7.0",
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
  }
@@ -8,7 +8,10 @@ module.exports = {
8
8
  es6: true,
9
9
  "node": true,
10
10
  },
11
+ parser: "@babel/eslint-parser",
11
12
  parserOptions: {
12
13
  sourceType: "module",
14
+ ecmaVersion: 2020,
15
+ requireConfigFile: false,
13
16
  },
14
17
  };
@@ -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
- fs.emptyDirSync(outputDir);
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
- ["@babel/plugin-transform-runtime",
59
- { "regenerator": true }]
70
+ [
71
+ "@babel/plugin-transform-runtime",
72
+ {
73
+ "regenerator": true,
74
+ "useESModules": true
75
+ }
76
+ ]
60
77
  ],
61
78
  }),
62
79
  ],
@@ -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,