From 3cccf99b0735396e3dfdb975c4c297246b462a0e Mon Sep 17 00:00:00 2001 From: Sergey Vartanov Date: Wed, 21 Jan 2026 20:47:35 +0400 Subject: [PATCH 1/2] Refactor project structure --- .githooks/check | 45 +++++---- .github/workflows/ci.yml | 23 +++-- .gitignore | 17 ++-- README.md | 97 ++++++------------- js/.prettierignore | 3 + .prettierrc => js/.prettierrc | 0 .stylelintignore => js/.stylelintignore | 0 .stylelintrc.json => js/.stylelintrc.json | 0 js/README.md | 54 +++++++++++ {doc => js/doc}/diagram.md | 0 {doc => js/doc}/public.moi | 0 {doc => js/doc}/syntax.moi | 0 eslint.config.mjs => js/eslint.config.mjs | 0 package-lock.json => js/package-lock.json | 0 package.json => js/package.json | 8 +- {scripts => js/scripts}/build-cli.ts | 12 ++- {scripts => js/scripts}/build-parser.ts | 1 + {scripts => js/scripts}/build-ui.ts | 0 {scripts => js/scripts}/generate-grammar.sh | 3 +- {src => js/src}/cli/generate-svg-wrapper.cjs | 0 {src => js/src}/cli/generate-svg.ts | 0 {src => js/src}/grammar.d.ts | 0 {src => js/src}/parser.ts | 8 +- {src => js/src}/types.d.ts | 0 {syntax => js/syntax}/README.md | 0 .../syntax}/iconscript-vscode/.vscodeignore | 0 .../language-configuration.json | 0 .../syntax}/iconscript-vscode/package.json | 0 .../syntax/iconscript.tmLanguage.json | 0 .../syntax}/iconscript.sublime-syntax | 0 {syntax => js/syntax}/iconscript.vim | 0 {test => js/test}/flag.iconscript | 0 {test => js/test}/main.iconscript | 0 tsconfig.json => js/tsconfig.json | 15 ++- {web => js/web}/bundles/.gitkeep | 0 {web => js/web}/index.html | 0 {web => js/web}/style.css | 0 {web => js/web}/ui.ts | 0 {web => js/web}/wrapper.css | 0 rust/README.md | 15 ++- 40 files changed, 168 insertions(+), 133 deletions(-) create mode 100644 js/.prettierignore rename .prettierrc => js/.prettierrc (100%) rename .stylelintignore => js/.stylelintignore (100%) rename .stylelintrc.json => js/.stylelintrc.json (100%) create mode 100644 js/README.md rename {doc => js/doc}/diagram.md (100%) rename {doc => js/doc}/public.moi (100%) rename {doc => js/doc}/syntax.moi (100%) rename eslint.config.mjs => js/eslint.config.mjs (100%) rename package-lock.json => js/package-lock.json (100%) rename package.json => js/package.json (92%) rename {scripts => js/scripts}/build-cli.ts (86%) rename {scripts => js/scripts}/build-parser.ts (95%) rename {scripts => js/scripts}/build-ui.ts (100%) rename {scripts => js/scripts}/generate-grammar.sh (84%) rename {src => js/src}/cli/generate-svg-wrapper.cjs (100%) rename {src => js/src}/cli/generate-svg.ts (100%) rename {src => js/src}/grammar.d.ts (100%) rename {src => js/src}/parser.ts (98%) rename {src => js/src}/types.d.ts (100%) rename {syntax => js/syntax}/README.md (100%) rename {syntax => js/syntax}/iconscript-vscode/.vscodeignore (100%) rename {syntax => js/syntax}/iconscript-vscode/language-configuration.json (100%) rename {syntax => js/syntax}/iconscript-vscode/package.json (100%) rename {syntax => js/syntax}/iconscript-vscode/syntax/iconscript.tmLanguage.json (100%) rename {syntax => js/syntax}/iconscript.sublime-syntax (100%) rename {syntax => js/syntax}/iconscript.vim (100%) rename {test => js/test}/flag.iconscript (100%) rename {test => js/test}/main.iconscript (100%) rename tsconfig.json => js/tsconfig.json (66%) rename {web => js/web}/bundles/.gitkeep (100%) rename {web => js/web}/index.html (100%) rename {web => js/web}/style.css (100%) rename {web => js/web}/ui.ts (100%) rename {web => js/web}/wrapper.css (100%) diff --git a/.githooks/check b/.githooks/check index c70efed..99f6d8c 100755 --- a/.githooks/check +++ b/.githooks/check @@ -2,6 +2,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +JS_DIR="$PROJECT_ROOT/js" if ! command -v antlr4 &> /dev/null && ! command -v antlr &> /dev/null; then echo "Error: ANTLR is not installed or not in PATH." >&2 @@ -13,59 +14,61 @@ echo "Cleaning up..." find "$PROJECT_ROOT/grammar" \ -type f \( -name "*.ts" -o -name "*.interp" -o -name "*.tokens" \) \ -delete -rm -f "$PROJECT_ROOT/web/bundles/parser.bundle.js" -rm -f "$PROJECT_ROOT/web/bundles/parser.bundle.min.js" -rm -f "$PROJECT_ROOT/web/bundles/parser.bundle.min.js.map" -rm -f "$PROJECT_ROOT/web/bundles/ui.bundle.js" -rm -f "$PROJECT_ROOT/web/bundles/ui.bundle.min.js" -rm -f "$PROJECT_ROOT/web/bundles/ui.bundle.min.js.map" -rm -rf "$PROJECT_ROOT/output" +rm -f "$JS_DIR/web/bundles/parser.bundle.js" +rm -f "$JS_DIR/web/bundles/parser.bundle.min.js" +rm -f "$JS_DIR/web/bundles/parser.bundle.min.js.map" +rm -f "$JS_DIR/web/bundles/ui.bundle.js" +rm -f "$JS_DIR/web/bundles/ui.bundle.min.js" +rm -f "$JS_DIR/web/bundles/ui.bundle.min.js.map" +rm -rf "$JS_DIR/output" echo "Generating grammar..." -"$PROJECT_ROOT/scripts/generate-grammar.sh" +"$JS_DIR/scripts/generate-grammar.sh" if [ ! -f "$PROJECT_ROOT/grammar/IconScriptLexer.ts" ]; then echo "Error: grammar was not created." >&2 exit 1 fi +cd "$JS_DIR" || exit 1 + echo "Building parser..." npm run build:parser:min # Check that `parser.bundle.min.js` was created. -if [ ! -f "$PROJECT_ROOT/web/bundles/parser.bundle.min.js" ]; then +if [ ! -f "$JS_DIR/web/bundles/parser.bundle.min.js" ]; then echo "Error: parser.bundle.min.js was not created." >&2 exit 1 fi echo "Creating SVG icons..." -mkdir -p "$PROJECT_ROOT/output" -mkdir -p "$PROJECT_ROOT/output/main" +mkdir -p "$JS_DIR/output" +mkdir -p "$JS_DIR/output/main" npm run generate -- \ - "$PROJECT_ROOT/test/main.iconscript" \ - "$PROJECT_ROOT/output/main" &> /dev/null -if [ ! -d "$PROJECT_ROOT/output" ]; then + "$JS_DIR/test/main.iconscript" \ + "$JS_DIR/output/main" &> /dev/null +if [ ! -d "$JS_DIR/output" ]; then echo "Error: output directory was not created." >&2 exit 1 fi -if [ ! -s "$PROJECT_ROOT/output" ]; then +if [ ! -s "$JS_DIR/output" ]; then echo "Error: output directory is empty." >&2 exit 1 fi echo "Creating command-line interface..." -rm -rf "$PROJECT_ROOT/dist" +rm -rf "$JS_DIR/dist" npm run build:cli # Check that `iconscript` was created. -if [ ! -f "$PROJECT_ROOT/dist/cli/generate-svg.js" ]; then +if [ ! -f "$JS_DIR/dist/cli/generate-svg.js" ]; then echo "Error: iconscript was not created." >&2 exit 1 fi echo "Building library..." -rm -rf "$PROJECT_ROOT/dist" +rm -rf "$JS_DIR/dist" npm run build:lib # Check that `dist/` was created. -if [ ! -f "$PROJECT_ROOT/dist/src/parser.js" ]; then - echo "Error: dist/src/parser.js was not created." >&2 +if [ ! -f "$JS_DIR/dist/js/src/parser.js" ]; then + echo "Error: dist/js/src/parser.js was not created." >&2 exit 1 fi @@ -98,4 +101,4 @@ echo "Running Rust tests..." if ! cargo test --manifest-path "$PROJECT_ROOT/rust/Cargo.toml"; then echo "Error: Rust tests failed." >&2 exit 1 -fi \ No newline at end of file +fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ed25ba5..f46fcf9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,8 +7,11 @@ on: branches: [main, master] jobs: - check: + js: runs-on: ubuntu-latest + defaults: + run: + working-directory: js steps: - name: Checkout code @@ -19,6 +22,7 @@ jobs: with: node-version: "20" cache: "npm" + cache-dependency-path: js/package-lock.json - name: Setup Java uses: actions/setup-java@v4 @@ -27,6 +31,7 @@ jobs: java-version: "17" - name: Install ANTLR + working-directory: . run: | wget -q https://www.antlr.org/download/antlr-4.13.2-complete.jar -O /tmp/antlr.jar echo '#!/bin/bash' > /tmp/antlr4 @@ -38,11 +43,12 @@ jobs: run: npm ci - name: Clean up generated files + working-directory: . run: | find grammar -type f \( -name "*.ts" -o -name "*.interp" -o -name "*.tokens" \) -delete || true - rm -f web/bundles/parser.bundle.js web/bundles/parser.bundle.min.js web/bundles/parser.bundle.min.js.map - rm -f web/bundles/ui.bundle.js web/bundles/ui.bundle.min.js web/bundles/ui.bundle.min.js.map - rm -rf output + rm -f js/web/bundles/parser.bundle.js js/web/bundles/parser.bundle.min.js js/web/bundles/parser.bundle.min.js.map + rm -f js/web/bundles/ui.bundle.js js/web/bundles/ui.bundle.min.js js/web/bundles/ui.bundle.min.js.map + rm -rf js/output - name: Generate grammar run: | @@ -50,6 +56,7 @@ jobs: ./scripts/generate-grammar.sh - name: Verify grammar generation + working-directory: . run: | if [ ! -f "grammar/IconScriptLexer.ts" ]; then echo "Error: grammar was not created." @@ -97,12 +104,12 @@ jobs: - name: Verify library build run: | - if [ ! -f "dist/src/parser.js" ]; then - echo "Error: dist/src/parser.js was not created." + if [ ! -f "dist/js/src/parser.js" ]; then + echo "Error: dist/js/src/parser.js was not created." exit 1 fi - if [ ! -f "dist/src/parser.d.ts" ]; then - echo "Error: dist/src/parser.d.ts was not created." + if [ ! -f "dist/js/src/parser.d.ts" ]; then + echo "Error: dist/js/src/parser.d.ts was not created." exit 1 fi diff --git a/.gitignore b/.gitignore index c45cc7d..893fb46 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,13 @@ *.egg-info/ -.npmrc +.env __pycache__/ grammar/.antlr/ -node_modules/ -# Output files. -dist/ -output/ +# JavaScript implementation. +js/.npmrc +js/node_modules/ +js/dist/ +js/output/ # Parsers, generated from `IconScript.g4` by ANTLR4. grammar/*.interp @@ -14,9 +15,9 @@ grammar/*.tokens grammar/*.ts # Bundled JavaScript files. -web/bundles/*.bundle.js -web/bundles/*.bundle.min.js -web/bundles/*.bundle.min.js.map +js/web/bundles/*.bundle.js +js/web/bundles/*.bundle.min.js +js/web/bundles/*.bundle.min.js.map # WIP. webassembly/ diff --git a/README.md b/README.md index 8a65d49..5545f2c 100644 --- a/README.md +++ b/README.md @@ -7,83 +7,42 @@ project. The grammar of the language is described in the ANTLR4 `grammar/IconScript.g4` file. -## Installation - -Install dependencies: - -```shell -npm install -``` - -## Development Setup - -Generate grammar files: - -```shell -npm run generate:grammar -``` - -Build all components (library, CLI, and web bundles): - -```shell -npm run build -``` - -Build individually: -- `npm run generate:grammar` — generate grammar files, -- `npm run build:lib` — build TypeScript library, -- `npm run build:cli` — build CLI tool, -- `npm run build:parser:min` — build parser bundle for web, -- `npm run build:ui:min` — build UI bundle for web. - -## Usage - -### Command-Line Interface - -```shell -npm run generate $INPUT_ICONSCRIPT_FILE $OUTPUT_DIR -``` - -### Web Interface - -1. Build the web bundles (if not already built): - -```shell -npm run build:parser:min -npm run build:ui:min -``` - -2. Start a local server (e.g., using `live-server`): - -```shell -npm install -g live-server -live-server web -``` - -Or use any other static file server pointing to the `web/` directory. +## SVG generation + +There are two implementations of iconscript for parsing and generating SVG +files. + - __Rust__: `cargo install iconscript`. Rust implementation is _faster_ and + _more reliable_. It uses + [`linesweeper`](https://docs.rs/linesweeper/latest/linesweeper/) library for + Boolean path operations and SVG optimizations. + - __JavaScript__ (TypeScript): `npm install iconscript`. JavaScript + implementation uses [Paper.js](http://paperjs.org/) library, that may produce + wrong outputs. ## Syntax +Syntax slightly resembles the syntax of path commands in SVG. + ### Global context - - __width__ — stroke width. - - __position__ — current position of the cursor. + - `width` — stroke width. + - `position` — current position of the cursor. ### Commands -`` is 2D coordinates in the form `,` or `+,` (`+` means +`` is 2D coordinates in the form `,` or `+,` (`+` means that the position is relative to the __position__). -| Command | Description | -|---|---| -| `subtract` | Set subtraction mode | -| `w ` | Set __width__ to a value | -| `m ` | Set __position__ to a value | -| `l []` | Draw lines between positions | -| `lf []` | Draw filled lines between positions | -| `e ` | Draw circle specified by center point and radius | -| `r ` | Draw rectangle specified by top left and bottom right points | -| `a ` | Draw arc specified by center point, radius, and two angles in radians | +| Command | Description | +| ------------------------------------ | --------------------------------------------------------------------------------- | +| `subtract` | Set subtraction mode | +| `w ` | Set `width` to a value | +| `m ` | Set `position` to a value | +| `l []` | Draw lines between positions | +| `lf []` | Draw filled lines between positions | +| `e ` | Draw circle specified by center point and radius | +| `r ` | Draw rectangle specified by top left and bottom right points | +| `a ` | Draw arc specified by center point, radius, start angle, and end angle in radians | ### Variables @@ -92,8 +51,8 @@ Variables can be defined with ` = []` and accessed with ### Scopes -Scopes group commands together using `{` and `}`. They can be nested and are used -to incapsulate context changes. +Scopes group commands together using `{` and `}`. They can be nested and are +used to incapsulate context changes. ### Example diff --git a/js/.prettierignore b/js/.prettierignore new file mode 100644 index 0000000..b967cb4 --- /dev/null +++ b/js/.prettierignore @@ -0,0 +1,3 @@ +dist/ +node_modules/ +web/bundles/ diff --git a/.prettierrc b/js/.prettierrc similarity index 100% rename from .prettierrc rename to js/.prettierrc diff --git a/.stylelintignore b/js/.stylelintignore similarity index 100% rename from .stylelintignore rename to js/.stylelintignore diff --git a/.stylelintrc.json b/js/.stylelintrc.json similarity index 100% rename from .stylelintrc.json rename to js/.stylelintrc.json diff --git a/js/README.md b/js/README.md new file mode 100644 index 0000000..00be9e0 --- /dev/null +++ b/js/README.md @@ -0,0 +1,54 @@ +## Installation + +Install dependencies: + +```shell +npm install +``` + +## Development Setup + +Generate grammar files: + +```shell +npm run generate:grammar +``` + +Build all components (library, CLI, and web bundles): + +```shell +npm run build +``` + +Build individually: +- `npm run generate:grammar` — generate grammar files, +- `npm run build:lib` — build TypeScript library, +- `npm run build:cli` — build CLI tool, +- `npm run build:parser:min` — build parser bundle for web, +- `npm run build:ui:min` — build UI bundle for web. + +## Usage + +### Command-Line Interface + +```shell +npm run generate $INPUT_ICONSCRIPT_FILE $OUTPUT_DIR +``` + +### Web Interface + +1. Build the web bundles (if not already built): + +```shell +npm run build:parser:min +npm run build:ui:min +``` + +2. Start a local server (e.g., using `live-server`): + +```shell +npm install -g live-server +live-server web +``` + +Or use any other static file server pointing to the `web/` directory. diff --git a/doc/diagram.md b/js/doc/diagram.md similarity index 100% rename from doc/diagram.md rename to js/doc/diagram.md diff --git a/doc/public.moi b/js/doc/public.moi similarity index 100% rename from doc/public.moi rename to js/doc/public.moi diff --git a/doc/syntax.moi b/js/doc/syntax.moi similarity index 100% rename from doc/syntax.moi rename to js/doc/syntax.moi diff --git a/eslint.config.mjs b/js/eslint.config.mjs similarity index 100% rename from eslint.config.mjs rename to js/eslint.config.mjs diff --git a/package-lock.json b/js/package-lock.json similarity index 100% rename from package-lock.json rename to js/package-lock.json diff --git a/package.json b/js/package.json similarity index 92% rename from package.json rename to js/package.json index 3e7eb4d..57a516e 100644 --- a/package.json +++ b/js/package.json @@ -3,12 +3,12 @@ "version": "0.2.0", "description": "iconscript, a language for generating SVG icons", "type": "module", - "main": "dist/parser.js", - "types": "dist/parser.d.ts", + "main": "dist/js/src/parser.js", + "types": "dist/js/src/parser.d.ts", "exports": { ".": { - "import": "./dist/parser.js", - "types": "./dist/parser.d.ts" + "import": "./dist/js/src/parser.js", + "types": "./dist/js/src/parser.d.ts" } }, "bin": { diff --git a/scripts/build-cli.ts b/js/scripts/build-cli.ts similarity index 86% rename from scripts/build-cli.ts rename to js/scripts/build-cli.ts index ade0a4b..63a503b 100644 --- a/scripts/build-cli.ts +++ b/js/scripts/build-cli.ts @@ -9,8 +9,9 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); async function buildCLI(): Promise { - const projectRoot = join(__dirname, ".."); - const outputFile = join(projectRoot, "dist", "cli", "generate-svg.js"); + const jsDir = join(__dirname, ".."); + const projectRoot = join(jsDir, ".."); + const outputFile = join(jsDir, "dist", "cli", "generate-svg.js"); const grammarLexer = join(projectRoot, "grammar", "IconScriptLexer.ts"); // Check if grammar files exist. @@ -29,7 +30,7 @@ async function buildCLI(): Promise { try { await build({ - entryPoints: [join(projectRoot, "src", "cli", "generate-svg.ts")], + entryPoints: [join(jsDir, "src", "cli", "generate-svg.ts")], bundle: true, platform: "node", format: "esm", @@ -42,18 +43,19 @@ async function buildCLI(): Promise { // Node.js dependencies (jsdom) that are difficult to bundle. // These will need to be installed when using the CLI. external: ["paper", "paperjs-offset"], + nodePaths: [join(jsDir, "node_modules")], }); console.log(`Created: ${basename(outputFile)}.`); // Copy the CommonJS wrapper for npm binary compatibility. const wrapperSource = join( - projectRoot, + jsDir, "src", "cli", "generate-svg-wrapper.cjs", ); const wrapperDest = join( - projectRoot, + jsDir, "dist", "cli", "generate-svg-wrapper.cjs", diff --git a/scripts/build-parser.ts b/js/scripts/build-parser.ts similarity index 95% rename from scripts/build-parser.ts rename to js/scripts/build-parser.ts index 75e3c2b..611728b 100644 --- a/scripts/build-parser.ts +++ b/js/scripts/build-parser.ts @@ -27,6 +27,7 @@ async function buildParser(): Promise { sourcemap: minify, target: "es2020", external: [], + nodePaths: [join(projectRoot, "node_modules")], }); console.log(`Created: ${basename(outputFile)}.`); } catch (error) { diff --git a/scripts/build-ui.ts b/js/scripts/build-ui.ts similarity index 100% rename from scripts/build-ui.ts rename to js/scripts/build-ui.ts diff --git a/scripts/generate-grammar.sh b/js/scripts/generate-grammar.sh similarity index 84% rename from scripts/generate-grammar.sh rename to js/scripts/generate-grammar.sh index 4af3f21..3fe0153 100755 --- a/scripts/generate-grammar.sh +++ b/js/scripts/generate-grammar.sh @@ -3,7 +3,8 @@ set -e -PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +JS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +PROJECT_ROOT="$(cd "$JS_DIR/.." && pwd)" GRAMMAR_FILE="$PROJECT_ROOT/grammar/IconScript.g4" OUTPUT_DIR="$PROJECT_ROOT/grammar" diff --git a/src/cli/generate-svg-wrapper.cjs b/js/src/cli/generate-svg-wrapper.cjs similarity index 100% rename from src/cli/generate-svg-wrapper.cjs rename to js/src/cli/generate-svg-wrapper.cjs diff --git a/src/cli/generate-svg.ts b/js/src/cli/generate-svg.ts similarity index 100% rename from src/cli/generate-svg.ts rename to js/src/cli/generate-svg.ts diff --git a/src/grammar.d.ts b/js/src/grammar.d.ts similarity index 100% rename from src/grammar.d.ts rename to js/src/grammar.d.ts diff --git a/src/parser.ts b/js/src/parser.ts similarity index 98% rename from src/parser.ts rename to js/src/parser.ts index 7e4fab3..69a3a88 100644 --- a/src/parser.ts +++ b/js/src/parser.ts @@ -8,9 +8,9 @@ import * as antlr4 from "antlr4"; import {ParseTreeWalker} from "antlr4"; import paper from "paper"; -import IconScriptLexer from "../grammar/IconScriptLexer"; -import IconScriptParser from "../grammar/IconScriptParser"; -import GeneratedIconScriptListener from "../grammar/IconScriptListener"; +import IconScriptLexer from "../../grammar/IconScriptLexer"; +import IconScriptParser from "../../grammar/IconScriptParser"; +import GeneratedIconScriptListener from "../../grammar/IconScriptListener"; import type {Icon} from "./types.js"; import type { AssignmentContext, @@ -27,7 +27,7 @@ import type { ScopeContext, PositionContext, CommandsContext, -} from "../grammar/IconScriptParser.js"; +} from "../../grammar/IconScriptParser.js"; const scale = 1.0; const defaultWidth = 1.0; diff --git a/src/types.d.ts b/js/src/types.d.ts similarity index 100% rename from src/types.d.ts rename to js/src/types.d.ts diff --git a/syntax/README.md b/js/syntax/README.md similarity index 100% rename from syntax/README.md rename to js/syntax/README.md diff --git a/syntax/iconscript-vscode/.vscodeignore b/js/syntax/iconscript-vscode/.vscodeignore similarity index 100% rename from syntax/iconscript-vscode/.vscodeignore rename to js/syntax/iconscript-vscode/.vscodeignore diff --git a/syntax/iconscript-vscode/language-configuration.json b/js/syntax/iconscript-vscode/language-configuration.json similarity index 100% rename from syntax/iconscript-vscode/language-configuration.json rename to js/syntax/iconscript-vscode/language-configuration.json diff --git a/syntax/iconscript-vscode/package.json b/js/syntax/iconscript-vscode/package.json similarity index 100% rename from syntax/iconscript-vscode/package.json rename to js/syntax/iconscript-vscode/package.json diff --git a/syntax/iconscript-vscode/syntax/iconscript.tmLanguage.json b/js/syntax/iconscript-vscode/syntax/iconscript.tmLanguage.json similarity index 100% rename from syntax/iconscript-vscode/syntax/iconscript.tmLanguage.json rename to js/syntax/iconscript-vscode/syntax/iconscript.tmLanguage.json diff --git a/syntax/iconscript.sublime-syntax b/js/syntax/iconscript.sublime-syntax similarity index 100% rename from syntax/iconscript.sublime-syntax rename to js/syntax/iconscript.sublime-syntax diff --git a/syntax/iconscript.vim b/js/syntax/iconscript.vim similarity index 100% rename from syntax/iconscript.vim rename to js/syntax/iconscript.vim diff --git a/test/flag.iconscript b/js/test/flag.iconscript similarity index 100% rename from test/flag.iconscript rename to js/test/flag.iconscript diff --git a/test/main.iconscript b/js/test/main.iconscript similarity index 100% rename from test/main.iconscript rename to js/test/main.iconscript diff --git a/tsconfig.json b/js/tsconfig.json similarity index 66% rename from tsconfig.json rename to js/tsconfig.json index c838439..235b821 100644 --- a/tsconfig.json +++ b/js/tsconfig.json @@ -11,15 +11,20 @@ "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "declaration": true, - "outDir": "./dist" + "outDir": "./dist", + "rootDir": "..", + "baseUrl": ".", + "paths": { + "antlr4": ["node_modules/antlr4"] + } }, "files": [ "src/parser.ts", "src/types.d.ts", "src/cli/generate-svg.ts", - "grammar/IconScriptLexer.ts", - "grammar/IconScriptParser.ts", - "grammar/IconScriptListener.ts" + "../grammar/IconScriptLexer.ts", + "../grammar/IconScriptParser.ts", + "../grammar/IconScriptListener.ts" ], - "exclude": ["node_modules", "dist", "**/*.js", "**/*.mjs", "scripts", "web", "grammar/*.interp", "grammar/*.tokens"] + "exclude": ["node_modules", "dist", "**/*.js", "**/*.mjs", "scripts", "web"] } \ No newline at end of file diff --git a/web/bundles/.gitkeep b/js/web/bundles/.gitkeep similarity index 100% rename from web/bundles/.gitkeep rename to js/web/bundles/.gitkeep diff --git a/web/index.html b/js/web/index.html similarity index 100% rename from web/index.html rename to js/web/index.html diff --git a/web/style.css b/js/web/style.css similarity index 100% rename from web/style.css rename to js/web/style.css diff --git a/web/ui.ts b/js/web/ui.ts similarity index 100% rename from web/ui.ts rename to js/web/ui.ts diff --git a/web/wrapper.css b/js/web/wrapper.css similarity index 100% rename from web/wrapper.css rename to js/web/wrapper.css diff --git a/rust/README.md b/rust/README.md index 0e8de9d..388aac4 100644 --- a/rust/README.md +++ b/rust/README.md @@ -17,16 +17,15 @@ The binary will be available at `target/release/iconscript`. ## Usage ```shell -./target/release/iconscript $OPTIONS $ICONSCRIPT_FILE +./target/release/iconscript $OPTIONS $ICONSCRIPT_FILE $OUTPUT_DIRECTORY ``` -| Option | Description | -| -------------------- | -------------------------------------------------- | -| `-o`, `--output` | Output directory for SVG files (default: `output`) | -| `-s`, `--sketch` | Output raw paths without combining | -| `--no-rounding` | Disable coordinate rounding | -| `--no-deduplication` | Disable duplicate point removal | -| `--no-collinear` | Disable collinear point simplification | +| Option | Description | +| -------------------- | -------------------------------------- | +| `-s`, `--sketch` | Output raw paths without combining | +| `--no-rounding` | Disable coordinate rounding | +| `--no-deduplication` | Disable duplicate point removal | +| `--no-collinear` | Disable collinear point simplification | ## Testing From f7cffd0af0686599ac0a5842b420d02634fafeb8 Mon Sep 17 00:00:00 2001 From: Sergey Vartanov Date: Wed, 21 Jan 2026 22:51:51 +0400 Subject: [PATCH 2/2] Refactor project structure 2 --- .githooks/check | 40 ++++++++++++++++++++++------------------ .github/workflows/ci.yml | 35 +++++++++++++++++++---------------- js/grammar | 1 + js/package.json | 10 +++++----- js/scripts/build-cli.ts | 19 +++++++++---------- js/src/parser.ts | 8 ++++---- js/tsconfig.json | 7 +++---- 7 files changed, 63 insertions(+), 57 deletions(-) create mode 120000 js/grammar diff --git a/.githooks/check b/.githooks/check index 99f6d8c..af07895 100755 --- a/.githooks/check +++ b/.githooks/check @@ -31,6 +31,11 @@ fi cd "$JS_DIR" || exit 1 +# Create grammar symlink if it doesn't exist. +if [ ! -L "$JS_DIR/grammar" ]; then + ln -s ../grammar "$JS_DIR/grammar" +fi + echo "Building parser..." npm run build:parser:min # Check that `parser.bundle.min.js` was created. @@ -39,27 +44,26 @@ if [ ! -f "$JS_DIR/web/bundles/parser.bundle.min.js" ]; then exit 1 fi -echo "Creating SVG icons..." +echo "Building command-line interface..." +rm -rf "$JS_DIR/dist" +npm run build:cli +# Check that CLI was created. +if [ ! -f "$JS_DIR/dist/cli/generate-svg.js" ]; then + echo "Error: dist/cli/generate-svg.js was not created." >&2 + exit 1 +fi + +echo "Creating SVG icons (testing CLI)..." mkdir -p "$JS_DIR/output" mkdir -p "$JS_DIR/output/main" -npm run generate -- \ +if ! npm run generate -- \ "$JS_DIR/test/main.iconscript" \ - "$JS_DIR/output/main" &> /dev/null -if [ ! -d "$JS_DIR/output" ]; then - echo "Error: output directory was not created." >&2 - exit 1 -fi -if [ ! -s "$JS_DIR/output" ]; then - echo "Error: output directory is empty." >&2 + "$JS_DIR/output/main"; then + echo "Error: SVG icon generation failed." >&2 exit 1 fi - -echo "Creating command-line interface..." -rm -rf "$JS_DIR/dist" -npm run build:cli -# Check that `iconscript` was created. -if [ ! -f "$JS_DIR/dist/cli/generate-svg.js" ]; then - echo "Error: iconscript was not created." >&2 +if [ ! -d "$JS_DIR/output/main" ] || [ -z "$(ls -A "$JS_DIR/output/main")" ]; then + echo "Error: output directory was not created or is empty." >&2 exit 1 fi @@ -67,8 +71,8 @@ echo "Building library..." rm -rf "$JS_DIR/dist" npm run build:lib # Check that `dist/` was created. -if [ ! -f "$JS_DIR/dist/js/src/parser.js" ]; then - echo "Error: dist/js/src/parser.js was not created." >&2 +if [ ! -f "$JS_DIR/dist/src/parser.js" ]; then + echo "Error: dist/src/parser.js was not created." >&2 exit 1 fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f46fcf9..f0dabe0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,6 +42,9 @@ jobs: - name: Install dependencies run: npm ci + - name: Create grammar symlink + run: ln -s ../grammar grammar + - name: Clean up generated files working-directory: . run: | @@ -73,18 +76,6 @@ jobs: exit 1 fi - - name: Create SVG icons (test) - run: | - mkdir -p output/main - npm run generate -- test/main.iconscript output/main - - - name: Verify SVG icons creation - run: | - if [ ! -d "output" ] || [ -z "$(ls -A output)" ]; then - echo "Error: output directory was not created or is empty." - exit 1 - fi - - name: Build CLI run: | rm -rf dist @@ -97,6 +88,18 @@ jobs: exit 1 fi + - name: Create SVG icons (test) + run: | + mkdir -p output/main + npm run generate -- test/main.iconscript output/main + + - name: Verify SVG icons creation + run: | + if [ ! -d "output/main" ] || [ -z "$(ls -A output/main)" ]; then + echo "Error: output directory was not created or is empty." + exit 1 + fi + - name: Build library run: | rm -rf dist @@ -104,12 +107,12 @@ jobs: - name: Verify library build run: | - if [ ! -f "dist/js/src/parser.js" ]; then - echo "Error: dist/js/src/parser.js was not created." + if [ ! -f "dist/src/parser.js" ]; then + echo "Error: dist/src/parser.js was not created." exit 1 fi - if [ ! -f "dist/js/src/parser.d.ts" ]; then - echo "Error: dist/js/src/parser.d.ts was not created." + if [ ! -f "dist/src/parser.d.ts" ]; then + echo "Error: dist/src/parser.d.ts was not created." exit 1 fi diff --git a/js/grammar b/js/grammar new file mode 120000 index 0000000..2744a25 --- /dev/null +++ b/js/grammar @@ -0,0 +1 @@ +../grammar \ No newline at end of file diff --git a/js/package.json b/js/package.json index 57a516e..8ec98b6 100644 --- a/js/package.json +++ b/js/package.json @@ -3,12 +3,12 @@ "version": "0.2.0", "description": "iconscript, a language for generating SVG icons", "type": "module", - "main": "dist/js/src/parser.js", - "types": "dist/js/src/parser.d.ts", + "main": "dist/src/parser.js", + "types": "dist/src/parser.d.ts", "exports": { ".": { - "import": "./dist/js/src/parser.js", - "types": "./dist/js/src/parser.d.ts" + "import": "./dist/src/parser.js", + "types": "./dist/src/parser.d.ts" } }, "bin": { @@ -27,7 +27,7 @@ "lint:css": "stylelint \"**/*.css\"", "lint": "npm run lint:ts && npm run lint:html && npm run lint:css", "test": "tsx test-suite.js", - "generate": "tsx src/cli/generate-svg.ts", + "generate": "node dist/cli/generate-svg.js", "build:cli": "tsx scripts/build-cli.ts", "build:lib": "tsc", "build:parser": "tsx scripts/build-parser.ts", diff --git a/js/scripts/build-cli.ts b/js/scripts/build-cli.ts index 63a503b..ac2e89e 100644 --- a/js/scripts/build-cli.ts +++ b/js/scripts/build-cli.ts @@ -9,9 +9,8 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); async function buildCLI(): Promise { - const jsDir = join(__dirname, ".."); - const projectRoot = join(jsDir, ".."); - const outputFile = join(jsDir, "dist", "cli", "generate-svg.js"); + const projectRoot = join(__dirname, ".."); + const outputFile = join(projectRoot, "dist", "cli", "generate-svg.js"); const grammarLexer = join(projectRoot, "grammar", "IconScriptLexer.ts"); // Check if grammar files exist. @@ -30,7 +29,7 @@ async function buildCLI(): Promise { try { await build({ - entryPoints: [join(jsDir, "src", "cli", "generate-svg.ts")], + entryPoints: [join(projectRoot, "src", "cli", "generate-svg.ts")], bundle: true, platform: "node", format: "esm", @@ -39,23 +38,23 @@ async function buildCLI(): Promise { banner: { js: "#!/usr/bin/env node", }, - // Mark paper and paperjs-offset as external since they have complex - // Node.js dependencies (jsdom) that are difficult to bundle. + // Mark dependencies as external since they have complex + // Node.js dependencies that are difficult to bundle. // These will need to be installed when using the CLI. - external: ["paper", "paperjs-offset"], - nodePaths: [join(jsDir, "node_modules")], + external: ["paper", "paperjs-offset", "antlr4"], + nodePaths: [join(projectRoot, "node_modules")], }); console.log(`Created: ${basename(outputFile)}.`); // Copy the CommonJS wrapper for npm binary compatibility. const wrapperSource = join( - jsDir, + projectRoot, "src", "cli", "generate-svg-wrapper.cjs", ); const wrapperDest = join( - jsDir, + projectRoot, "dist", "cli", "generate-svg-wrapper.cjs", diff --git a/js/src/parser.ts b/js/src/parser.ts index 69a3a88..7e4fab3 100644 --- a/js/src/parser.ts +++ b/js/src/parser.ts @@ -8,9 +8,9 @@ import * as antlr4 from "antlr4"; import {ParseTreeWalker} from "antlr4"; import paper from "paper"; -import IconScriptLexer from "../../grammar/IconScriptLexer"; -import IconScriptParser from "../../grammar/IconScriptParser"; -import GeneratedIconScriptListener from "../../grammar/IconScriptListener"; +import IconScriptLexer from "../grammar/IconScriptLexer"; +import IconScriptParser from "../grammar/IconScriptParser"; +import GeneratedIconScriptListener from "../grammar/IconScriptListener"; import type {Icon} from "./types.js"; import type { AssignmentContext, @@ -27,7 +27,7 @@ import type { ScopeContext, PositionContext, CommandsContext, -} from "../../grammar/IconScriptParser.js"; +} from "../grammar/IconScriptParser.js"; const scale = 1.0; const defaultWidth = 1.0; diff --git a/js/tsconfig.json b/js/tsconfig.json index 235b821..706d5b3 100644 --- a/js/tsconfig.json +++ b/js/tsconfig.json @@ -12,7 +12,6 @@ "resolveJsonModule": true, "declaration": true, "outDir": "./dist", - "rootDir": "..", "baseUrl": ".", "paths": { "antlr4": ["node_modules/antlr4"] @@ -22,9 +21,9 @@ "src/parser.ts", "src/types.d.ts", "src/cli/generate-svg.ts", - "../grammar/IconScriptLexer.ts", - "../grammar/IconScriptParser.ts", - "../grammar/IconScriptListener.ts" + "grammar/IconScriptLexer.ts", + "grammar/IconScriptParser.ts", + "grammar/IconScriptListener.ts" ], "exclude": ["node_modules", "dist", "**/*.js", "**/*.mjs", "scripts", "web"] } \ No newline at end of file