From 892d621f94929a84b7fd77e3aa446966e524d829 Mon Sep 17 00:00:00 2001 From: Anindya Kundu Date: Tue, 15 Feb 2022 02:28:24 +0530 Subject: [PATCH 01/14] changelog: move to project root --- src/CHANGELOG.md => CHANGELOG.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/CHANGELOG.md => CHANGELOG.md (100%) diff --git a/src/CHANGELOG.md b/CHANGELOG.md similarity index 100% rename from src/CHANGELOG.md rename to CHANGELOG.md From 785779ec80ae916ab69c8a4cd508213f088a2e08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 19 Feb 2022 15:35:35 +0000 Subject: [PATCH 02/14] build(deps): bump url-parse from 1.5.4 to 1.5.7 Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.4 to 1.5.7. - [Release notes](https://github.com/unshiftio/url-parse/releases) - [Commits](https://github.com/unshiftio/url-parse/compare/1.5.4...1.5.7) --- updated-dependencies: - dependency-name: url-parse dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 590bcd57..9de53302 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20316,9 +20316,10 @@ } }, "node_modules/url-parse": { - "version": "1.5.4", + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz", + "integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==", "dev": true, - "license": "MIT", "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -36025,7 +36026,9 @@ } }, "url-parse": { - "version": "1.5.4", + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz", + "integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==", "dev": true, "requires": { "querystringify": "^2.1.1", From 6d85a155a9d933d59550fd3d04da7977c3dcfd0a Mon Sep 17 00:00:00 2001 From: Anindya Kundu Date: Mon, 28 Feb 2022 01:14:54 +0530 Subject: [PATCH 03/14] editor: [feat] use YAML for representing programs - adds js-yaml package to load YAML syntax to JSON - generates snapshot from YAML code --- package-lock.json | 26 +-- package.json | 2 + src/components/editor/@types/index.ts | 13 ++ src/components/editor/core/dummy.ts | 156 ----------------- src/components/editor/core/index.ts | 243 ++++++++++++++++++-------- src/components/editor/index.ts | 54 ++++-- 6 files changed, 240 insertions(+), 254 deletions(-) create mode 100644 src/components/editor/@types/index.ts delete mode 100644 src/components/editor/core/dummy.ts diff --git a/package-lock.json b/package-lock.json index 9de53302..666357f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@sugarlabs/musicblocks-v4-lib": "^0.2.0", "jquery": "^3.6.0", + "js-yaml": "^3.14.1", "p5": "^1.4.0", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -25,6 +26,7 @@ "@types/jest": "^27.4.0", "@types/jquery": "^3.5.8", "@types/jqueryui": "^1.12.16", + "@types/js-yaml": "^4.0.5", "@types/node": "^16.11.21", "@types/p5": "^1.3.3", "@types/react": "^17.0.38", @@ -3332,6 +3334,12 @@ "@types/jquery": "*" } }, + "node_modules/@types/js-yaml": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", + "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.9", "dev": true, @@ -4177,7 +4185,6 @@ }, "node_modules/argparse": { "version": "1.0.10", - "dev": true, "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" @@ -8137,7 +8144,6 @@ }, "node_modules/esprima": { "version": "4.0.1", - "dev": true, "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", @@ -11887,7 +11893,6 @@ }, "node_modules/js-yaml": { "version": "3.14.1", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -18290,7 +18295,6 @@ }, "node_modules/sprintf-js": { "version": "1.0.3", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/ssri": { @@ -24523,6 +24527,12 @@ "@types/jquery": "*" } }, + "@types/js-yaml": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", + "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", + "dev": true + }, "@types/json-schema": { "version": "7.0.9", "dev": true @@ -25084,7 +25094,6 @@ }, "argparse": { "version": "1.0.10", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -27768,8 +27777,7 @@ } }, "esprima": { - "version": "4.0.1", - "dev": true + "version": "4.0.1" }, "esquery": { "version": "1.4.0", @@ -30312,7 +30320,6 @@ }, "js-yaml": { "version": "3.14.1", - "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -34685,8 +34692,7 @@ } }, "sprintf-js": { - "version": "1.0.3", - "dev": true + "version": "1.0.3" }, "ssri": { "version": "8.0.1", diff --git a/package.json b/package.json index ed2d028f..1f119de3 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@types/jest": "^27.4.0", "@types/jquery": "^3.5.8", "@types/jqueryui": "^1.12.16", + "@types/js-yaml": "^4.0.5", "@types/node": "^16.11.21", "@types/p5": "^1.3.3", "@types/react": "^17.0.38", @@ -47,6 +48,7 @@ "dependencies": { "@sugarlabs/musicblocks-v4-lib": "^0.2.0", "jquery": "^3.6.0", + "js-yaml": "^3.14.1", "p5": "^1.4.0", "react": "^17.0.2", "react-dom": "^17.0.2", diff --git a/src/components/editor/@types/index.ts b/src/components/editor/@types/index.ts new file mode 100644 index 00000000..f69632bb --- /dev/null +++ b/src/components/editor/@types/index.ts @@ -0,0 +1,13 @@ +/** Represents the interface for a code instruction snapshot object. */ +export interface ICodeInstructionSnapshotObj { + [key: string]: + | { + [key: string]: boolean | number | string | ICodeInstructionSnapshotObj[]; + } + | boolean + | number + | string; +} + +/** Represents the interface for a code instruction snapshot. */ +export type ICodeInstructionSnapshot = string | ICodeInstructionSnapshotObj; diff --git a/src/components/editor/core/dummy.ts b/src/components/editor/core/dummy.ts deleted file mode 100644 index 6dcfd20c..00000000 --- a/src/components/editor/core/dummy.ts +++ /dev/null @@ -1,156 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ - -import { - generateFromSnapshot, - generateSnapshot, - getInstance, - getNode, -} from '@sugarlabs/musicblocks-v4-lib'; - -/* - * dummy program for debugging - * - * set-thickness value:4 - * set-color value:5 - * repeat times:6 - * move-forward steps:100 - * turn-right angle:60 - * set-color value:9 - * repeat times:6 - * move-forward steps:100 - * turn-left angle:60 - */ -generateFromSnapshot({ - process: [], - routine: [], - crumbs: [ - [ - { - elementName: 'set-color', - argMap: { - value: { - elementName: 'value-number', - }, - }, - }, - { - elementName: 'set-thickness', - argMap: { - value: { - elementName: 'value-number', - }, - }, - }, - { - elementName: 'repeat', - argMap: { - times: { - elementName: 'value-number', - }, - }, - scope: [ - { - elementName: 'move-forward', - argMap: { - steps: { - elementName: 'value-number', - }, - }, - }, - { - elementName: 'turn-right', - argMap: { - angle: { - elementName: 'value-number', - }, - }, - }, - ], - }, - { - elementName: 'set-color', - argMap: { - value: { - elementName: 'value-number', - }, - }, - }, - { - elementName: 'set-thickness', - argMap: { - value: { - elementName: 'value-number', - }, - }, - }, - { - elementName: 'repeat', - argMap: { - times: { - elementName: 'value-number', - }, - }, - scope: [ - { - elementName: 'move-forward', - argMap: { - steps: { - elementName: 'value-number', - }, - }, - }, - { - elementName: 'turn-left', - argMap: { - angle: { - elementName: 'value-number', - }, - }, - }, - ], - }, - ], - ], -}); - -getInstance( - // @ts-ignore - getNode(generateSnapshot().crumbs[0][0]['argMap']['value'].nodeID)!.instanceID, -)!.instance.updateLabel('5'); -getInstance( - // @ts-ignore - getNode(generateSnapshot().crumbs[0][1]['argMap']['value'].nodeID)!.instanceID, -)!.instance.updateLabel('4'); -getInstance( - // @ts-ignore - getNode(generateSnapshot().crumbs[0][2]['argMap']['times'].nodeID)!.instanceID, -)!.instance.updateLabel('6'); -getInstance( - // @ts-ignore - getNode(generateSnapshot().crumbs[0][2]['scope'][0]['argMap']['steps'].nodeID)!.instanceID, -)!.instance.updateLabel('200'); -getInstance( - // @ts-ignore - getNode(generateSnapshot().crumbs[0][2]['scope'][1]['argMap']['angle'].nodeID)!.instanceID, -)!.instance.updateLabel('60'); - -getInstance( - // @ts-ignore - getNode(generateSnapshot().crumbs[0][3]['argMap']['value'].nodeID)!.instanceID, -)!.instance.updateLabel('9'); -getInstance( - // @ts-ignore - getNode(generateSnapshot().crumbs[0][4]['argMap']['value'].nodeID)!.instanceID, -)!.instance.updateLabel('4'); -getInstance( - // @ts-ignore - getNode(generateSnapshot().crumbs[0][5]['argMap']['times'].nodeID)!.instanceID, -)!.instance.updateLabel('6'); -getInstance( - // @ts-ignore - getNode(generateSnapshot().crumbs[0][5]['scope'][0]['argMap']['steps'].nodeID)!.instanceID, -)!.instance.updateLabel('200'); -getInstance( - // @ts-ignore - getNode(generateSnapshot().crumbs[0][5]['scope'][1]['argMap']['angle'].nodeID)!.instanceID, -)!.instance.updateLabel('60'); diff --git a/src/components/editor/core/index.ts b/src/components/editor/core/index.ts index d0b9c505..414292fb 100644 --- a/src/components/editor/core/index.ts +++ b/src/components/editor/core/index.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { load as yamlToJson } from 'js-yaml'; import { ITreeSnapshotInput, @@ -9,15 +9,41 @@ import { getSpecificationSnapshot, } from '@sugarlabs/musicblocks-v4-lib'; +/** @todo these should be exposed */ +import { IElementSpecificationSnapshot } from '@sugarlabs/musicblocks-v4-lib/@types/specification'; +/** @todo these should be exposed */ +import { + ITreeSnapshotDataInput, + ITreeSnapshotExpressionInput, + ITreeSnapshotStatementInput, + ITreeSnapshotBlockInput, +} from '@sugarlabs/musicblocks-v4-lib/@types/syntaxTree'; +/** @todo these should not be required */ import { addInstance, getInstance, removeInstance, } from '@sugarlabs/musicblocks-v4-lib/syntax/warehouse/warehouse'; +import { ICodeInstructionSnapshot, ICodeInstructionSnapshotObj } from '../@types'; + import { librarySpecification } from '@sugarlabs/musicblocks-v4-lib'; registerElementSpecificationEntries(librarySpecification); +// -- private variables ---------------------------------------------------------------------------- + +interface IElementSpecificationSnapshotWithArgs extends IElementSpecificationSnapshot { + args: [string, string | string[]][] | null; +} + +/** + * Snapshot entry table object with key-value pairs of element name and corresponding element + * specification snapshot. + */ +let _specificationSnapshot: { + [name: string]: IElementSpecificationSnapshotWithArgs; +}; + // -- public functions ----------------------------------------------------------------------------- /** @@ -25,26 +51,35 @@ registerElementSpecificationEntries(librarySpecification); * @returns list of valid instruction signatures */ export function generateAPI(): string { - const snapshot = getSpecificationSnapshot(); + _specificationSnapshot = Object.fromEntries( + Object.entries(getSpecificationSnapshot()) + .filter(([_, specification]) => ['Statement', 'Block'].includes(specification.type)) + .map(([elementName, specification]) => [elementName, { ...specification, args: null }]), + ); const api: string[] = []; - Object.entries(snapshot) - .filter( - ([_, specification]) => - specification.type === 'Statement' && - ['Graphics', 'Pen'].includes(specification.category), - ) - .forEach(([elementName, _]) => { - const instanceID = addInstance(elementName); - const instance = getInstance(instanceID)!.instance; - const args: [string, string][] = instance.argLabels.map((arg) => [ - arg, - instance.getArgType(arg).join('|'), - ]); - removeInstance(instanceID); - - api.push(`${elementName} ${args.map(([name, types]) => `${name}:${types}`).join(' ')}`); - }); + Object.entries(_specificationSnapshot).forEach(([elementName, _]) => { + /** @todo args should be part of the specification */ + + const instanceID = addInstance(elementName); + const instance = getInstance(instanceID)!.instance; + + const args: [string, string][] = instance.argLabels.map((arg) => [ + arg, + instance.getArgType(arg).join('|'), + ]); + _specificationSnapshot[elementName]['args'] = + instance.argLabels.length === 0 + ? null + : (instance.argLabels.map((arg) => [arg, instance.getArgType(arg)]) as [ + string, + string | string[], + ][]); + + removeInstance(instanceID); + + api.push(`${elementName} ${args.map(([name, types]) => `${name}:${types}`).join(' ')}`); + }); return api.join('\n'); } @@ -55,71 +90,141 @@ export function generateAPI(): string { * @returns a `Promise` that returns whether the process was successful */ export function buildProgram(code: string): Promise { - function checkValidity(): boolean { - /* - * dummy logic - */ - const lines = code.split('\n'); - for (const line of lines) { - const units = line.split(' '); - if ( - !( - units.length === 1 || - units.length === 2 || - units.length === 3 || - (units.length === 4 && units[0] === '' && units[1] === '') - ) - ) { - return false; - } + let instructions: ICodeInstructionSnapshot[]; + + function __checkValidity(): boolean { + try { + instructions = yamlToJson(code) as ICodeInstructionSnapshot[]; + return instructions instanceof Array; + } catch (e) { + const _err = e as { + mark: { + buffer: string; + column: number; + line: number; + name: string | null; + position: number; + }; + message: string; + name: string; + reason: string; + }; + console.log({ + mark: _err.mark, + message: _err.message, + name: _err.name, + reason: _err.reason, + }); + return false; } - - return true; } - function transpile(): void { - // dummy program build (for debugging) - // import('./dummy'); - - const snapshot: ITreeSnapshotInput = { process: [], routine: [], crumbs: [[]] }; - const crumb = snapshot.crumbs[0]; - - const addInstruction = (units: string[]) => { - const elementName = units[0]; - const argMap = {}; - - let args: [string, string][] = units - .slice(1) - .map((unit) => unit.split(':') as [string, string]); - args.forEach(([param, value]) => { - // @ts-ignore - argMap[param] = { - elementName: 'value-number', - value, + function __transpile(): void { + function __createInstructionSnapshot( + instruction: ICodeInstructionSnapshot, + ): ITreeSnapshotStatementInput | ITreeSnapshotBlockInput { + if (typeof instruction === 'string') { + return { + elementName: instruction, + argMap: null, }; - }); + } else { + const treeSnapshotInput: ITreeSnapshotStatementInput | ITreeSnapshotBlockInput = { + elementName: '', + argMap: null, + }; + const [key, value] = [ + // there's only one instruction + Object.keys(instruction)[0], + instruction[Object.keys(instruction)[0]], + ]; + + treeSnapshotInput.elementName = key; + + if (value instanceof Object) { + let scope: (ITreeSnapshotStatementInput | ITreeSnapshotBlockInput)[] | null = + null; + if ('scope' in value) { + scope = (value.scope as ICodeInstructionSnapshotObj[]).map((instruction) => + __createInstructionSnapshot(instruction), + ); + } + + const argMap: { + [argName: string]: + | ITreeSnapshotDataInput + | ITreeSnapshotExpressionInput + | null; + } = {}; + Object.entries(value).forEach(([param, arg]) => { + if (param !== 'scope') { + if (!['boolean', 'number', 'string'].includes(typeof arg)) { + throw Error( + `InvalidArgumentError: ${arg} of type "${typeof arg}" is invalid`, + ); + } + + const type = + typeof arg === 'boolean' + ? 'value-boolean' + : typeof arg === 'number' + ? 'value-number' + : 'value-string'; + argMap[param] = { + elementName: type, + value: arg.toString(), + }; + } + }); + treeSnapshotInput.argMap = argMap; + + if (scope) { + (treeSnapshotInput as ITreeSnapshotBlockInput).scope = scope; + } + } else { + if ( + _specificationSnapshot[key].args && + _specificationSnapshot[key].args!.length === 1 + ) { + const type = + typeof value === 'boolean' + ? 'value-boolean' + : typeof value === 'number' + ? 'value-number' + : 'value-string'; + + treeSnapshotInput.argMap = Object.fromEntries([ + [ + _specificationSnapshot[key].args![0][0], + { + elementName: type, + value: value.toString(), + }, + ], + ]); + } + } + + return treeSnapshotInput; + } + } - crumb.push({ - elementName, - argMap, - }); + const snapshot: ITreeSnapshotInput = { + process: [], + routine: [], + crumbs: [instructions.map((instruction) => __createInstructionSnapshot(instruction))], }; - for (const line of code.split('\n')) { - const units = line.split(' '); - addInstruction(units); - } - generateFromSnapshot(snapshot); } return new Promise((resolve) => { - if (checkValidity() === false) { + if (__checkValidity() === false) { resolve(false); } else { const snapshot = generateSnapshot(); try { - transpile(); + __transpile(); resolve(true); } catch (e) { console.log(e); diff --git a/src/components/editor/index.ts b/src/components/editor/index.ts index 35e7136a..05ff845e 100644 --- a/src/components/editor/index.ts +++ b/src/components/editor/index.ts @@ -41,26 +41,42 @@ export function setup(): Promise { }); } - setCode(`set-thickness value:4 -set-color value:5 -repeat times:6 - move-forward steps:100 - turn-right angle:60 -set-color value:9 -repeat times:6 - move-forward steps:100 - turn-left angle:60`); + setCode(`- clear +- set-thickness: + value: 4 +- set-color: + value: 5 +- repeat: + times: 6 + scope: + - move-forward: + steps: 100 + - turn-right: + angle: 60 +- set-color: + value: 9 +- repeat: + times: 6 + scope: + - move-forward: + steps: 100 + - turn-left: + angle: 60`); - setCode(`set-thickness value:4 -set-color value:5 -move-forward steps:100 -turn-right angle:60 -move-forward steps:100 -turn-right angle:60 -move-forward steps:100 -turn-right angle:60 -move-forward steps:100 -turn-right angle:60`); + setCode(`- clear +- set-thickness: 4 +- set-color: 5 +- repeat: + times: 6 + scope: + - move-forward: 100 + - turn-right: 60 +- set-color: 9 +- repeat: + times: 6 + scope: + - move-forward: 100 + - turn-left: 60`); setHelp(generateAPI()); From 8010f9296112aa4055fae2fe6b2aa831305bd275 Mon Sep 17 00:00:00 2001 From: Anindya Kundu Date: Mon, 28 Feb 2022 01:57:34 +0530 Subject: [PATCH 04/14] editor: [feat] update API for YAML syntax --- src/components/editor/core/index.ts | 82 ++++++++++++++++++++++++----- src/components/editor/index.ts | 30 ++++------- src/config/index.ts | 7 ++- 3 files changed, 83 insertions(+), 36 deletions(-) diff --git a/src/components/editor/core/index.ts b/src/components/editor/core/index.ts index 414292fb..0cc97970 100644 --- a/src/components/editor/core/index.ts +++ b/src/components/editor/core/index.ts @@ -4,7 +4,6 @@ import { ITreeSnapshotInput, generateFromSnapshot, generateSnapshot, - registerElementSpecificationEntries, resetSyntaxTree, getSpecificationSnapshot, } from '@sugarlabs/musicblocks-v4-lib'; @@ -27,13 +26,10 @@ import { import { ICodeInstructionSnapshot, ICodeInstructionSnapshotObj } from '../@types'; -import { librarySpecification } from '@sugarlabs/musicblocks-v4-lib'; -registerElementSpecificationEntries(librarySpecification); - // -- private variables ---------------------------------------------------------------------------- interface IElementSpecificationSnapshotWithArgs extends IElementSpecificationSnapshot { - args: [string, string | string[]][] | null; + args: [string, string[]][] | null; } /** @@ -51,34 +47,92 @@ let _specificationSnapshot: { * @returns list of valid instruction signatures */ export function generateAPI(): string { + /* + * Filter out only instruction (statement, block) elements in specification snapshot. + */ + _specificationSnapshot = Object.fromEntries( Object.entries(getSpecificationSnapshot()) .filter(([_, specification]) => ['Statement', 'Block'].includes(specification.type)) .map(([elementName, specification]) => [elementName, { ...specification, args: null }]), ); - const api: string[] = []; - Object.entries(_specificationSnapshot).forEach(([elementName, _]) => { - /** @todo args should be part of the specification */ + /** + * @todo args should be part of the supplied specification snapshot + * Add args to the specification. + */ + Object.entries(_specificationSnapshot).forEach(([elementName, _]) => { const instanceID = addInstance(elementName); const instance = getInstance(instanceID)!.instance; - const args: [string, string][] = instance.argLabels.map((arg) => [ - arg, - instance.getArgType(arg).join('|'), - ]); _specificationSnapshot[elementName]['args'] = instance.argLabels.length === 0 ? null : (instance.argLabels.map((arg) => [arg, instance.getArgType(arg)]) as [ string, - string | string[], + string[], ][]); removeInstance(instanceID); + }); + + /** + * Group syntax elements by categories. + */ + + const items: { + [key: string]: [string, 'Statement' | 'Block', [string, string[]][] | null][]; + } = {}; + + Object.entries(_specificationSnapshot).forEach(([elementName, specification]) => { + const category = specification.category; + + if (!(category in items)) { + items[category] = []; + } - api.push(`${elementName} ${args.map(([name, types]) => `${name}:${types}`).join(' ')}`); + items[category].push([ + elementName, + specification.type as 'Statement' | 'Block', + specification.args, + ]); + }); + + /** + * Generate API. + */ + + const api: string[] = []; + + Object.entries(items).forEach(([category, elements]) => { + api.push(`# "${category}" elements\n# ------------------------`); + elements.forEach(([name, type, args]) => { + if (type === 'Statement') { + if (args === null) { + api.push(`- ${name}`); + } else if (args.length === 1) { + api.push(`- ${name}: ${args[0][1].join('|')}`); + } else { + api.push( + `- ${name}:\n${args + .map(([name, types]) => ` ${name}: ${types.join('|')}`) + .join('\n')}`, + ); + } + } else { + if (args === null) { + api.push(`- ${name}:\n scope:\n - [instruction]\n - ...`); + } else { + api.push( + `- ${name}:\n${args + .map(([name, types]) => ` ${name}: ${types.join('|')}`) + .join('\n')}\n scope:\n - [instruction]\n - ...`, + ); + } + } + }); + api.push('\n'); }); return api.join('\n'); diff --git a/src/components/editor/index.ts b/src/components/editor/index.ts index 05ff845e..7f56c934 100644 --- a/src/components/editor/index.ts +++ b/src/components/editor/index.ts @@ -42,28 +42,11 @@ export function setup(): Promise { } setCode(`- clear -- set-thickness: - value: 4 -- set-color: - value: 5 -- repeat: - times: 6 - scope: - - move-forward: - steps: 100 - - turn-right: - angle: 60 -- set-color: - value: 9 -- repeat: - times: 6 - scope: - - move-forward: - steps: 100 - - turn-left: - angle: 60`); - setCode(`- clear +# ------------- +# first hexagon +# ------------- + - set-thickness: 4 - set-color: 5 - repeat: @@ -71,6 +54,11 @@ export function setup(): Promise { scope: - move-forward: 100 - turn-right: 60 + +# -------------- +# second hexagon +# -------------- + - set-color: 9 - repeat: times: 6 diff --git a/src/config/index.ts b/src/config/index.ts index f506420c..7fd084f7 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,6 +1,9 @@ import { IConfig, IComponent } from '../@types'; -import { registerElementSpecificationEntries } from '@sugarlabs/musicblocks-v4-lib'; +import { + registerElementSpecificationEntries, + librarySpecification, +} from '@sugarlabs/musicblocks-v4-lib'; // -- private variables ---------------------------------------------------------------------------- @@ -239,6 +242,8 @@ export function getComponent(name: string): IComponent | null { }); Promise.all(importPromises).then((components) => { + registerElementSpecificationEntries(librarySpecification); + const iterator = components.entries(); const setupComponent = ( From 993cac58bd058601933a35f6bd993013bd07fc40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Feb 2022 04:43:55 +0000 Subject: [PATCH 05/14] build(deps): bump url-parse from 1.5.7 to 1.5.10 Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.7 to 1.5.10. - [Release notes](https://github.com/unshiftio/url-parse/releases) - [Commits](https://github.com/unshiftio/url-parse/compare/1.5.7...1.5.10) --- updated-dependencies: - dependency-name: url-parse dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9de53302..5342a27a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20316,9 +20316,9 @@ } }, "node_modules/url-parse": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz", - "integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==", + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", "dev": true, "dependencies": { "querystringify": "^2.1.1", @@ -36026,9 +36026,9 @@ } }, "url-parse": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz", - "integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==", + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", "dev": true, "requires": { "querystringify": "^2.1.1", From 44bc6468f25c8452b736c9286e7d92fa84b083a1 Mon Sep 17 00:00:00 2001 From: Anindya Kundu Date: Sat, 5 Mar 2022 02:58:36 +0530 Subject: [PATCH 06/14] editor: [feat] add support for data and expression elements --- src/components/editor/@types/index.ts | 27 ++- src/components/editor/core/errors.ts | 30 +++ src/components/editor/core/index.ts | 335 ++++++++++++++++++-------- 3 files changed, 284 insertions(+), 108 deletions(-) create mode 100644 src/components/editor/core/errors.ts diff --git a/src/components/editor/@types/index.ts b/src/components/editor/@types/index.ts index f69632bb..a068fb89 100644 --- a/src/components/editor/@types/index.ts +++ b/src/components/editor/@types/index.ts @@ -1,13 +1,18 @@ -/** Represents the interface for a code instruction snapshot object. */ -export interface ICodeInstructionSnapshotObj { - [key: string]: - | { - [key: string]: boolean | number | string | ICodeInstructionSnapshotObj[]; - } - | boolean - | number - | string; +/** Reresents a literal code argument. */ +export type ICodeArgumentLiteral = boolean | number | string; + +/** Represents the interface for a code argument snapshot object. */ +export interface ICodeArgumentObj { + [key: string]: ICodeArgumentLiteral | ICodeArgumentObj; +} + +/** Represents the interface for a code argument element. */ +export type ICodeArgument = ICodeArgumentLiteral | ICodeArgumentObj; + +/** Represents the interface for a code instruction element object. */ +export interface ICodeInstructionObj { + [instruction: string]: ICodeArgument; } -/** Represents the interface for a code instruction snapshot. */ -export type ICodeInstructionSnapshot = string | ICodeInstructionSnapshotObj; +/** Represents the interface for a code instruction element. */ +export type ICodeInstruction = string | ICodeInstructionObj; diff --git a/src/components/editor/core/errors.ts b/src/components/editor/core/errors.ts new file mode 100644 index 00000000..acc28dd9 --- /dev/null +++ b/src/components/editor/core/errors.ts @@ -0,0 +1,30 @@ +abstract class SyntaxError extends Error { + private _name: string; + private _message: string; + + constructor(name: string, message: string) { + super(message); + this._name = name; + this._message = message; + } + + public toString(): string { + return `${this._name}: ${this._message}`; + } + + public get type(): string { + return this._name; + } +} + +export class InvalidInstructionError extends SyntaxError { + constructor(message: string) { + super('InvalidInstructionError', message); + } +} + +export class InvalidArgumentError extends SyntaxError { + constructor(message: string) { + super('InvalidArgumentError', message); + } +} diff --git a/src/components/editor/core/index.ts b/src/components/editor/core/index.ts index 0cc97970..2f064882 100644 --- a/src/components/editor/core/index.ts +++ b/src/components/editor/core/index.ts @@ -9,7 +9,10 @@ import { } from '@sugarlabs/musicblocks-v4-lib'; /** @todo these should be exposed */ -import { IElementSpecificationSnapshot } from '@sugarlabs/musicblocks-v4-lib/@types/specification'; +import { + TElementType, + IElementSpecificationSnapshot, +} from '@sugarlabs/musicblocks-v4-lib/@types/specification'; /** @todo these should be exposed */ import { ITreeSnapshotDataInput, @@ -24,7 +27,9 @@ import { removeInstance, } from '@sugarlabs/musicblocks-v4-lib/syntax/warehouse/warehouse'; -import { ICodeInstructionSnapshot, ICodeInstructionSnapshotObj } from '../@types'; +import { ICodeArgumentObj, ICodeArgument, ICodeInstruction } from '../@types'; + +import { InvalidArgumentError, InvalidInstructionError } from './errors'; // -- private variables ---------------------------------------------------------------------------- @@ -47,14 +52,11 @@ let _specificationSnapshot: { * @returns list of valid instruction signatures */ export function generateAPI(): string { - /* - * Filter out only instruction (statement, block) elements in specification snapshot. - */ - _specificationSnapshot = Object.fromEntries( - Object.entries(getSpecificationSnapshot()) - .filter(([_, specification]) => ['Statement', 'Block'].includes(specification.type)) - .map(([elementName, specification]) => [elementName, { ...specification, args: null }]), + Object.entries(getSpecificationSnapshot()).map(([elementName, specification]) => [ + elementName, + { ...specification, args: null }, + ]), ); /** @@ -82,7 +84,7 @@ export function generateAPI(): string { */ const items: { - [key: string]: [string, 'Statement' | 'Block', [string, string[]][] | null][]; + [key: string]: [string, TElementType, [string, string[]][] | null][]; } = {}; Object.entries(_specificationSnapshot).forEach(([elementName, specification]) => { @@ -92,11 +94,7 @@ export function generateAPI(): string { items[category] = []; } - items[category].push([ - elementName, - specification.type as 'Statement' | 'Block', - specification.args, - ]); + items[category].push([elementName, specification.type, specification.args]); }); /** @@ -108,7 +106,25 @@ export function generateAPI(): string { Object.entries(items).forEach(([category, elements]) => { api.push(`# "${category}" elements\n# ------------------------`); elements.forEach(([name, type, args]) => { - if (type === 'Statement') { + if (type === 'Data') { + api.push(`- [instruction]:\n [param]:\n ${name}: string`); + } else if (type === 'Expression') { + if (args === null) { + // not possible + } else if (args.length === 1) { + api.push( + `- [instruction]:\n ${name}:\n${args + .map(([name, types]) => ` ${name}: ${types.join('|')}`) + .join('\n')}`, + ); + } else { + api.push( + `- [instruction]:\n [param]:\n ${name}:\n${args + .map(([name, types]) => ` ${name}: ${types.join('|')}`) + .join('\n')}`, + ); + } + } else if (type === 'Statement') { if (args === null) { api.push(`- ${name}`); } else if (args.length === 1) { @@ -120,7 +136,7 @@ export function generateAPI(): string { .join('\n')}`, ); } - } else { + } else if (type === 'Block') { if (args === null) { api.push(`- ${name}:\n scope:\n - [instruction]\n - ...`); } else { @@ -144,11 +160,11 @@ export function generateAPI(): string { * @returns a `Promise` that returns whether the process was successful */ export function buildProgram(code: string): Promise { - let instructions: ICodeInstructionSnapshot[]; + let instructions: ICodeInstruction[]; - function __checkValidity(): boolean { + function __verifyStructureValidity(): boolean { try { - instructions = yamlToJson(code) as ICodeInstructionSnapshot[]; + instructions = yamlToJson(code) as ICodeInstruction[]; return instructions instanceof Array; } catch (e) { const _err = e as { @@ -174,106 +190,231 @@ export function buildProgram(code: string): Promise { } function __transpile(): void { - function __createInstructionSnapshot( - instruction: ICodeInstructionSnapshot, - ): ITreeSnapshotStatementInput | ITreeSnapshotBlockInput { - if (typeof instruction === 'string') { + function __findSingleArgParam(instruction: string): string { + const args = _specificationSnapshot[instruction].args; + if (args === null) { + throw new InvalidArgumentError(`"${instruction}" does not take arguments`); + } else if (args.length !== 1) { + throw new InvalidArgumentError( + `"${instruction}" takes ${args.length} arguments but only 1 argument supplied`, + ); + } + + return args[0][0]; + } + + function __verifyArgParams(instruction: string, argObj: ICodeArgument): void { + const args = _specificationSnapshot[instruction].args; + + if (args === null) { + throw new InvalidArgumentError(`"${instruction}" does not take arguments`); + } + + const isBlock = _specificationSnapshot[instruction].type === 'Block'; + + const exptParams = args.map(([param, _]) => param); + const origParams = Object.keys(argObj); + + if (isBlock) { + if (!origParams.includes('scope')) { + throw new InvalidInstructionError(`"${instruction}" expects a scope`); + } + + if (!((argObj as ICodeArgumentObj)['scope'] instanceof Array)) { + throw new InvalidInstructionError( + `"${instruction}" supplied with invalid scope`, + ); + } + + exptParams.push('scope'); + } + + if (exptParams.length !== origParams.length) { + throw new InvalidArgumentError( + `invalid number of arguments supplied for "${instruction}"`, + ); + } + + if (exptParams.filter((param) => !origParams.includes(param)).length !== 0) { + throw new InvalidArgumentError(`invalid arguments supplied for "${instruction}"`); + } + } + + function __buildInputSnapshotArgument( + instruction: string, + param: string, + codeArgument: ICodeArgument, + ): ITreeSnapshotDataInput | ITreeSnapshotExpressionInput { + // code argument is a literal + if (typeof codeArgument !== 'object') { return { - elementName: instruction, + elementName: + typeof codeArgument === 'boolean' + ? 'value-boolean' + : typeof codeArgument === 'number' + ? 'value-number' + : 'value-string', + value: codeArgument.toString(), + }; + } + + if (Object.keys(codeArgument).length !== 1) { + throw new InvalidArgumentError( + `invalid argument for parameter "${param}" of "${instruction}"`, + ); + } + + // there's only one arg element + const [argElem, args] = Object.entries(codeArgument)[0]; + + // single arg but supplied as key-value pair + if (param === argElem) { + return __buildInputSnapshotArgument(instruction, param, args); + } + + if (!(argElem in _specificationSnapshot)) { + throw new InvalidInstructionError( + `"${argElem}" is not a valid argument for "${argElem}"`, + ); + } + + const specification = _specificationSnapshot[argElem]; + if (specification.type === 'Data') { + if (typeof args === 'string') { + return { + elementName: argElem, + value: args, + }; + } + + throw new InvalidArgumentError(`invalid argument for 'Data' element "${argElem}"`); + } else if (specification.type === 'Expression') { + const treeSnapshotInputArgument: ITreeSnapshotExpressionInput = { + elementName: '', argMap: null, }; + + treeSnapshotInputArgument.elementName = argElem; + + // single arg + if (typeof args !== 'object' || Object.keys(args).length === 1) { + // can throw error + const param = __findSingleArgParam(argElem); + + treeSnapshotInputArgument.argMap = Object.fromEntries([ + [param, __buildInputSnapshotArgument(argElem, param, args)], + ]); + } + // multiple args + else { + // can throw error + __verifyArgParams(argElem, args); + + treeSnapshotInputArgument.argMap = Object.fromEntries( + Object.entries(args).map(([param, arg]) => [ + param, + __buildInputSnapshotArgument(argElem, param, arg), + ]), + ); + } + + return treeSnapshotInputArgument; } else { - const treeSnapshotInput: ITreeSnapshotStatementInput | ITreeSnapshotBlockInput = { + throw new InvalidArgumentError( + `"${argElem}" is not a 'Data' or 'Expression' element`, + ); + } + } + + function __buildInputSnapshotInstruction( + codeInstruction: ICodeInstruction, + ): ITreeSnapshotStatementInput | ITreeSnapshotBlockInput { + // code instruction is a string + if (typeof codeInstruction === 'string') { + if (!(codeInstruction in _specificationSnapshot)) { + throw new InvalidInstructionError( + `"${codeInstruction}" is not a valid instruction`, + ); + } + + return { + elementName: codeInstruction, + argMap: null, + }; + } + // code instruction is an object + else { + if (Object.keys(codeInstruction).length !== 1) { + throw new InvalidInstructionError('wrong instruction format encountered'); + } + + // there's only one instruction + const [instruction, args] = Object.entries(codeInstruction)[0]; + + if (!(instruction in _specificationSnapshot)) { + throw new InvalidInstructionError( + `"${codeInstruction}" is not a valid instruction`, + ); + } + + const treeSnapshotInputInstruction: + | ITreeSnapshotStatementInput + | ITreeSnapshotBlockInput = { elementName: '', argMap: null, }; - const [key, value] = [ - // there's only one instruction - Object.keys(instruction)[0], - instruction[Object.keys(instruction)[0]], - ]; - - treeSnapshotInput.elementName = key; - - if (value instanceof Object) { - let scope: (ITreeSnapshotStatementInput | ITreeSnapshotBlockInput)[] | null = - null; - if ('scope' in value) { - scope = (value.scope as ICodeInstructionSnapshotObj[]).map((instruction) => - __createInstructionSnapshot(instruction), - ); - } - const argMap: { - [argName: string]: - | ITreeSnapshotDataInput - | ITreeSnapshotExpressionInput - | null; - } = {}; - Object.entries(value).forEach(([param, arg]) => { - if (param !== 'scope') { - if (!['boolean', 'number', 'string'].includes(typeof arg)) { - throw Error( - `InvalidArgumentError: ${arg} of type "${typeof arg}" is invalid`, - ); - } - - const type = - typeof arg === 'boolean' - ? 'value-boolean' - : typeof arg === 'number' - ? 'value-number' - : 'value-string'; - argMap[param] = { - elementName: type, - value: arg.toString(), - }; - } - }); - treeSnapshotInput.argMap = argMap; - - if (scope) { - (treeSnapshotInput as ITreeSnapshotBlockInput).scope = scope; - } - } else { - if ( - _specificationSnapshot[key].args && - _specificationSnapshot[key].args!.length === 1 - ) { - const type = - typeof value === 'boolean' - ? 'value-boolean' - : typeof value === 'number' - ? 'value-number' - : 'value-string'; - - treeSnapshotInput.argMap = Object.fromEntries([ - [ - _specificationSnapshot[key].args![0][0], - { - elementName: type, - value: value.toString(), - }, - ], - ]); + treeSnapshotInputInstruction.elementName = instruction; + + // single arg + if (typeof args !== 'object' || Object.keys(args).length === 1) { + // can throw error + const param = __findSingleArgParam(instruction); + + treeSnapshotInputInstruction.argMap = Object.fromEntries([ + [param, __buildInputSnapshotArgument(instruction, param, args)], + ]); + } + // multiple args + else { + // can throw error + __verifyArgParams(instruction, args); + + treeSnapshotInputInstruction.argMap = Object.fromEntries( + Object.entries(args) + .filter(([param, _]) => param !== 'scope') + .map(([param, arg]) => [ + param, + __buildInputSnapshotArgument(instruction, param, arg), + ]), + ); + + if ('scope' in args) { + (treeSnapshotInputInstruction as ITreeSnapshotBlockInput).scope = ( + args['scope'] as unknown as ICodeInstruction[] + ).map((item) => __buildInputSnapshotInstruction(item)); } } - return treeSnapshotInput; + return treeSnapshotInputInstruction; } } const snapshot: ITreeSnapshotInput = { process: [], routine: [], - crumbs: [instructions.map((instruction) => __createInstructionSnapshot(instruction))], + crumbs: [ + (instructions as ICodeInstruction[]).map((instruction) => + __buildInputSnapshotInstruction(instruction), + ), + ], }; generateFromSnapshot(snapshot); } return new Promise((resolve) => { - if (__checkValidity() === false) { + if (__verifyStructureValidity() === false) { resolve(false); } else { const snapshot = generateSnapshot(); From b4d10d9d802c32ef3684103ea0152ef37667c4d8 Mon Sep 17 00:00:00 2001 From: Anindya Kundu Date: Sat, 5 Mar 2022 02:59:38 +0530 Subject: [PATCH 07/14] editor: [view] update example code --- src/components/editor/index.ts | 50 +++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/components/editor/index.ts b/src/components/editor/index.ts index 7f56c934..32944f20 100644 --- a/src/components/editor/index.ts +++ b/src/components/editor/index.ts @@ -50,7 +50,10 @@ export function setup(): Promise { - set-thickness: 4 - set-color: 5 - repeat: - times: 6 + times: + operator-math-plus: + operand1: 4 + operand2: 2 scope: - move-forward: 100 - turn-right: 60 @@ -66,6 +69,51 @@ export function setup(): Promise { - move-forward: 100 - turn-left: 60`); + setCode(`- box-number: + name: "a" + value: 0 +- box-number: + name: "b" + value: 1 +- box-number: + name: "c" + value: 0 +- set-thickness: + value: + 4 +- repeat: + times: 10 + scope: + - print: + boxidentifier-number: "a" + - set-color: + boxidentifier-number: "b" + - repeat: + times: 6 + scope: + - move-forward: + operator-math-times: + operand1: + boxidentifier-number: "a" + operand2: 8 + - turn-left: 90 + - box-number: + name: "c" + value: + operator-math-plus: + operand1: + boxidentifier-number: "a" + operand2: + boxidentifier-number: "b" + - box-number: + name: "a" + value: + boxidentifier-number: "b" + - box-number: + name: "b" + value: + boxidentifier-number: "c"`); + setHelp(generateAPI()); const btn = getElement('button'); From ccb13581d530f11d53f6b5faff9736254ca34081 Mon Sep 17 00:00:00 2001 From: Anindya Kundu Date: Sat, 5 Mar 2022 02:59:51 +0530 Subject: [PATCH 08/14] view: [toolbar] remove hardcoded width --- src/components/editor/view/components/index.scss | 2 +- src/view/components/toolbar/index.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/editor/view/components/index.scss b/src/components/editor/view/components/index.scss index a571348a..02363696 100644 --- a/src/components/editor/view/components/index.scss +++ b/src/components/editor/view/components/index.scss @@ -1,7 +1,7 @@ @import '@/scss/colors'; #editor { - width: 100%; + width: 26rem; height: 100%; .editor-wrapper { diff --git a/src/view/components/toolbar/index.scss b/src/view/components/toolbar/index.scss index 8ee0ffbc..12b0209a 100644 --- a/src/view/components/toolbar/index.scss +++ b/src/view/components/toolbar/index.scss @@ -61,7 +61,7 @@ display: flex; flex-direction: column; box-sizing: border-box; - width: 20rem; + max-width: 30rem; height: 100%; padding: 0.5rem; From 863d1a389cdef1fb076a56405ad2d2e236be77c5 Mon Sep 17 00:00:00 2001 From: Akshat Agarwal Date: Sun, 27 Feb 2022 22:29:24 +0530 Subject: [PATCH 09/14] docs: [README] specify node.js version using node.js v16 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 21ab14b1..95e41667 100644 --- a/README.md +++ b/README.md @@ -181,7 +181,7 @@ deployment on a web browser. ### Without Docker This is a _**TypeScript**_ project that uses _**React**_. You'll just need -_[**Node.js**](https://nodejs.org/en/)_ and _**npm**_ installed on your development machine_. +_[**Node.js**](https://nodejs.org/en/) v16_ and _**npm**_ installed on your development machine_. Although, this is sufficient to run, build, and test the project as a whole, you might need some extra tools for other development tasks. From 820257829cbd11b9b7fa0429d1d27a7ac0975f3b Mon Sep 17 00:00:00 2001 From: Anindya Kundu Date: Sat, 5 Mar 2022 06:38:26 +0530 Subject: [PATCH 10/14] docs: [README] update docker development image tag renamed musicblocks:4.0.0-dev to musicblocks:4-dev --- README.md | 14 +++++++------- docker-compose.yml | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 95e41667..f08ec6a6 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,7 @@ of ideas (Sugar Labs is a meritocracy)._ ## Tech Stack -Music Blocks v4 shall be built using `TypeScript 4`and `React 17 (with hooks)`. In addition, `SCSS` +Music Blocks v4 shall be built using `TypeScript 4` and `React 17 (with hooks)`. In addition, `SCSS` shall be used for styling; `Webpack` will be configured to transpile and bundle the source code, for deployment on a web browser. @@ -181,7 +181,7 @@ deployment on a web browser. ### Without Docker This is a _**TypeScript**_ project that uses _**React**_. You'll just need -_[**Node.js**](https://nodejs.org/en/) v16_ and _**npm**_ installed on your development machine_. +_[**Node.js**](https://nodejs.org/en/) v16_ and _**npm**_ installed on your development machine. Although, this is sufficient to run, build, and test the project as a whole, you might need some extra tools for other development tasks. @@ -241,13 +241,13 @@ Windows) this repository using 4. Build _docker image_ and launch _docker network_. _**Note:**_ A - [built initial development image](https://github.com/sugarlabs/musicblocks-v4/pkgs/container/musicblocks/2948273?tag=4.0.0-dev) + [built initial development image](https://github.com/sugarlabs/musicblocks-v4/pkgs/container/musicblocks/16217005?tag=4-dev) has been published to [_Sugar Labs GitHub Container Registry_ (_GHCR_)](https://github.com/orgs/sugarlabs/packages?ecosystem=container), which can be pulled directly, so you don't have to build it again. Pull using ```bash - docker pull ghcr.io/sugarlabs/musicblocks:4.0.0-dev + docker pull ghcr.io/sugarlabs/musicblocks:4-dev ``` Nagivate inside the project directory and launch the _docker network_ using @@ -270,11 +270,11 @@ Windows) this repository using 5. In a second terminal, run ```bash - docker attach musicblocks-4.0.0-dev + docker attach musicblocks-4-dev ``` - The _Alpine shell_ in the _docker container_ named _musicblocks-4.0.0-dev_ is spawned and - standard input/output is connected to the terminal. + The _Alpine shell_ in the _docker container_ named _musicblocks-4-dev_ is spawned and standard + input/output is connected to the terminal. 6. _**Node.js**_ (_Node.js Runtime_), _**npm**_ (_Node.js Package Manager_), _**tsc**_ (_TypeScript Compiler_), _**ts-node**_ (_Node.js executable for TypeScript_), and _**http-server**_ (_a HTTP diff --git a/docker-compose.yml b/docker-compose.yml index 6867dfc3..a1ffd083 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: musicblocks: build: ./ - image: ghcr.io/sugarlabs/musicblocks:4.0.0-dev + image: ghcr.io/sugarlabs/musicblocks:4-dev env_file: - ./env/development.env ports: @@ -10,6 +10,6 @@ services: - "5001:80" volumes: - ./:/app/ - container_name: musicblocks-4.0.0-dev + container_name: musicblocks-4-dev stdin_open: true tty: true From 4a5a5fe367d73faab04a4a9873ff44deea1b72f4 Mon Sep 17 00:00:00 2001 From: Anindya Kundu Date: Sat, 5 Mar 2022 06:38:53 +0530 Subject: [PATCH 11/14] docs: [README] update binary package versions --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f08ec6a6..e3918399 100644 --- a/README.md +++ b/README.md @@ -209,11 +209,11 @@ node -v && npm -v && tsc -v && ts-node -v && http-server -v Output should look like ```bash -v14.17.0 -6.14.13 -Version 4.3.2 -v10.0.0 -v0.12.3 +v16.14.0 +8.3.1 +Version 4.6.2 +v10.6.0 +v14.1.0 ``` ### With Docker @@ -287,11 +287,11 @@ Windows) this repository using Output should look like ```bash - v14.17.0 - 6.14.13 - Version 4.3.2 - v10.0.0 - v0.12.3 + v16.14.0 + 8.3.1 + Version 4.6.2 + v10.6.0 + v14.1.0 ``` 7. To shut down the _docker network_, run (in the terminal where you ran `docker-compose up -d` or From 25c62d5c9a21fd3a7d24329a93714abae3ee32d5 Mon Sep 17 00:00:00 2001 From: Akshat Agarwal Date: Thu, 17 Feb 2022 01:20:28 +0530 Subject: [PATCH 12/14] docs: [README] remove outdated text Removed line - Currently this will open a page with a "Hello world!" message. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 21ab14b1..a6ce631f 100644 --- a/README.md +++ b/README.md @@ -357,8 +357,6 @@ After you are set-up, the steps you take depend on what you want to do: on host. Visit `localhost:5000` in a browser to view the web page served. If you are not using the container, visit `localhost:3000`. - Currently this will open a page with a "Hello world!" message. - - For testing, run ```bash From 40b307c77b0037af7bb6c752c63c3d827f3bf3f3 Mon Sep 17 00:00:00 2001 From: Anindya Kundu Date: Sat, 5 Mar 2022 07:00:10 +0530 Subject: [PATCH 13/14] chore: bump package version to 4.1.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 086b4a69..a564f29b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "musicblocks", - "version": "4.0.1", + "version": "4.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "musicblocks", - "version": "4.0.1", + "version": "4.1.0", "license": "AGPL-3.0", "dependencies": { "@sugarlabs/musicblocks-v4-lib": "^0.2.0", diff --git a/package.json b/package.json index 1f119de3..0bc26718 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "musicblocks", - "version": "4.0.1", + "version": "4.1.0", "description": "A complete overhaul of Music Blocks", "private": true, "repository": { From 2fe96dad399d129799116de4126fb4cfcc88ad38 Mon Sep 17 00:00:00 2001 From: Anindya Kundu Date: Sat, 5 Mar 2022 07:04:40 +0530 Subject: [PATCH 14/14] changelog: add v4.1.0 details --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a9f8014..afac3ce4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 4.1.0 [2022-03-05] + +- Updates README text. +- Adds the feature to author programs in the _Editor_ using _YAML_. +- Updates vulnerable dependent package `url-parse` version. + ## 4.0.1 [2022-02-15] - Updates core dependency `musicblocks-v4-lib` version from `1.0.1` to prerelease `0.2.0`.