diff --git a/.gitignore b/.gitignore
index 0cd3179..c62f6e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,8 @@
/raw/
/dist/
+/.bynix-build-*/
/node_modules/
*.js
!js/*.js
/.trash/
-*.tgz
\ No newline at end of file
+*.tgz
diff --git a/README.md b/README.md
index 56f8607..b5ddcd0 100644
--- a/README.md
+++ b/README.md
@@ -25,20 +25,35 @@ npm install -g bynixscript
### Using NPM
Run a BynixScript file:
```
-bsr path/to/script.bs
+bynix run path/to/script.bys
```
Compile a BynixScript file:
```
-bst path/to/script.bs
+bynix compile path/to/script.bys
```
-To print a BynixScript file to console:
+The compiled JavaScript is written next to the source file (`script.js`, or `script.mjs` for `.mbs`).
+To print translated JavaScript without running it:
```
-bsp path/to/script.bs
+bynix print path/to/script.bys
```
-To delete a file
+To delete a file (an explicit file path is required):
```
-bsd path/to/script.bs
+bynix delete path/to/script.bys
```
+Run `bynix --help` or `bynix run --help` for usage. The default supported extensions are `.bys`, `.bynixscript`, and `.mbs`.
+
+To build the packaged executable from source while developing:
+```
+npm ci
+npm run build
+npm test
+```
+The build bootstraps the `.bs` CLI sources with the BynixScript compiler from the
+`bynixscript` development dependency, then bundles the compiled JavaScript with
+esbuild into `dist/index.min.cjs`. `npm test` also checks that every CLI source
+compiles through the same compiler path.
+The build also bundles the shared compiler with the browser integration entry
+into `dist/browser.js`; browser configuration uses the default extensions.
### Using CDN
```html
diff --git a/docs/markdown/command.md b/docs/markdown/command.md
index a799299..2e9ded2 100644
--- a/docs/markdown/command.md
+++ b/docs/markdown/command.md
@@ -1,47 +1,16 @@
# Command
-BynixScript has commands that can help you use BynixScript more easily.
-1. **Execution command**
- - **bsr command**:
- This command is used to run BynixScript without compiling first:
- ```
- bsr path/to/script.bs
- ```
- - **bst command**:
- This command is used to compile the BynixScript file:
- ```
- bst path/to/script.bs
- ```
- - **bsp command**:
- This command is used to print the results of the translation of the BynixScript code to the cli:
- ```
- bsp path/to/script.bs
- ```
- - **bsd command**:
- This command is used to delete files:
- ```
- bsd path/to/script.bs
- ```
-2. **Help command**
- - **Display help**:
- If you want to see help, you can write:
- ```
- bynixscript --help
-
- # or shorter
- bs -h
- ```
- - **Display version**:
- If you want to see the current version of BynixScript you are using, you can see it with the command:
- ```
- bynixscript --version
-
- # or shorter
- bs -v
- ```
- - **Display download**:
- ```
- bynixscript --download [--day, --week, --month]
-
- # or shorter
- bs -d [-d, -w, -m]
- ```
\ No newline at end of file
+
+After `npm install -g bynixscript`, use the installed `bynix` executable:
+
+| Command | Action |
+| --- | --- |
+| `bynix run path/to/script.bys` | Translate and run a BynixScript file. |
+| `bynix compile path/to/script.bys` | Write translated JavaScript beside the source file (`.js`, or `.mjs` for `.mbs`). Does not run the file. |
+| `bynix print path/to/script.bys` | Print translated JavaScript without running the file. |
+| `bynix delete path/to/script.bys` | Delete the specified file. A path is always required. |
+
+By default, input files use `.bys`, `.bynixscript`, or `.mbs`. Paths are relative to the current directory, or may be absolute. Invalid paths and unsupported extensions produce an error and a nonzero exit status.
+
+Use `bynix --help` for all commands, `bynix --help` for command-specific help, and `bynix --version` for the version. The earlier option forms `bynix -r `, `bynix -c `, `bynix -p `, and `bynix -d ` remain available.
+
+For development, run `npm ci` followed by `npm run build`. The build uses the `bynixscript` development dependency as a bootstrap compiler to translate the `.bs` CLI sources through BynixScript's normal parser and replacement passes, then bundles the resulting JavaScript with esbuild into `dist/index.min.cjs`. `npm test` builds the executable, checks that all CLI sources compile through that same path, and runs the command tests.
diff --git a/js/browser.js b/js/browser.js
index 88af974..dfb68f1 100644
--- a/js/browser.js
+++ b/js/browser.js
@@ -1,382 +1,29 @@
-let parsingResults;
-let allowJs;
-const style = document.createElement("style");
-style.textContent = `
- bynix {
- display: none;
- }
- `;
-style.classList.add("bynix-style");
-document.head.appendChild(style);
-const milown = document.createElement("script");
-const ajax = document.createElement("script");
-const fontawesome = document.createElement("link");
-const brython = document.createElement("script");
-milown.src = '';
-ajax.src = 'https://cdnjs.cloudflare.com/ajax/libs/jquery/4.0.0-beta.2/jquery.min.js"';
-fontawesome.rel = "stylesheet";
-fontawesome.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css';
-brython.type = "type/javascript";
-brython.src = 'https://cdn.jsdelivr.net/npm/brython@3.9.6/brython.min.js';
-document.head.appendChild(ajax);
-document.head.appendChild(fontawesome);
-document.head.appendChild(milown);
-document.head.appendChild(brython);
-document.addEventListener("DOMContentLoaded", function() {
- const bynixTags = document.querySelectorAll("bynix");
- bynixTags.forEach(tag => {
- var code = '';
- const src = tag.getAttribute('src');
- const allowJsTag = tag.getAttribute('allowJs');
-
- if (allowJsTag === null) {
- allowJs = "false";
- } else {
- allowJs = allowJsTag;
- }
-
- // Jika src ada, lakukan fetch
- if (src) {
- const end = src.substring(src.lastIndexOf('.') + 1);
-
- if (src.endsWith(".bs") || src.endsWith(".bynixscript") || src.endsWith(".mbs")) {
- fetch(src)
- .then(response => response.text())
- .then(srcCode => {
- code = parseCode(srcCode);
- parsingResults = parsing(code);
- code = addSemicolons(code);
- executeCode(code);
- tag.remove();
- })
- .catch(error => console.error('Error fetching BynixScript code:', error));
- } else {
- console.error("Invalid extension '." + end + "'");
- }
- } else {
- // Jika src tidak ada, ambil kode dari textContent
- code = parseCode(tag.textContent);
- parsingResults = parsing(code);
- code = addSemicolons(code);
- executeCode(code);
- tag.remove();
- }
- });
-});
-
-function executeCode(code) {
- if (parsingResults === "false") {
- console.error(`Please check your code and try again.`);
- } else {
- // function
- code = code.replace(/func\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(([^)]*)\):/g, (match, p1, p2) => `function ${p1}(${p2}) {`);
-
- // if, elif, else
- code = code.replace(/elif\s+(.+?):/g, (match, p1) => `} else if (${p1}) {`);
- code = code.replace(/if\s+(.+?):/g, (match, p1) => `if (${p1}) {`);
- code = code.replace(/else:/g, "} else {");
-
- // forEach
- code = code.replace(/forEach\((.+?)\)\:/g, (match, p1) => `forEach(${p1} => {`);
- code = code.replace(/forEach\((.+?)\s=>\s(.+?)\)/g, (match, p1, p2) => `forEach(${p1} => ${p2})`);
-
- // var, const
- code = code.replace(/var\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+?);/g, (match, p1, p2) => `var ${p1} = ${p2};`);
- code = code.replace(/const\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+?);/g, (match, p1, p2) => `const ${p1} = ${p2};`);
-
- // print, print.err, print.info, print.debug, print.trace, print.warn, print.assert
- code = code.replace(/print(\.[a-zA-Z]+)?\((.+?)\)/g, (match, p1, p2) => {
- const type = p1 || '';
-
- if (type.includes(".info") || type.includes(".debug") || type.includes(".trace") || type.includes(".warn") || type.includes(".assert")) {
- return `console${p1}(${p2})`
- } else if (type.includes(".err")) {
- return `console.error(${p2})`
- } else if (match.includes("/")) {
- return match;
- } else {
- return `console.log(${p2})`
+const { compileSource } = require('../scripts/compile.cjs')
+
+const style = document.createElement('style')
+style.textContent = 'bynix { display: none; }'
+style.classList.add('bynix-style')
+document.head.appendChild(style)
+
+document.addEventListener('DOMContentLoaded', () => {
+ document.querySelectorAll('bynix').forEach(async tag => {
+ const sourcePath = tag.getAttribute('src')
+ try {
+ if (sourcePath && !['.bs', '.bys', '.bynixscript', '.mbs'].some(extension => sourcePath.endsWith(extension))) {
+ throw new Error(`Unsupported BynixScript file: ${sourcePath}`)
}
- });
-
- // touch, ask, confirm
- code = code.replace(/touch\((.+?)\)/g, (match, p1) => `alert(${p1});`);
- code = code.replace(/ask\((.+?)\)/g, (match, p1) => `prompt(${p1});`);
- code = code.replace(/confirm\((.+?)\)/g, (match, p1) => `confirm(${p1});`);
-
- // rand, roundDown, roundUp, maxOf, minOf
- code = code.replace(/rand\((.+?)\)/g, (match, p1) => `Math.random(${p1})`);
- code = code.replace(/roundDown\((.+?)\)/g, (match, p1) => `Math.floor(${p1})`);
- code = code.replace(/roundUp\((.+?)\)/g, (match, p1) => `Math.ceil(${p1})`);
- code = code.replace(/maxOf\((.+?)\)/g, (match, p1) => `Math.min(${p1})`);
- code = code.replace(/minOf\((.+?)\)/g, (match, p1) => `Math.max(${p1})`);
-
- // getElement, getAllElement, createElement, addClass, addElement, listener
- code = code.replace(/getElement\((.+?)\)/g, (match, p1) => `document.querySelector(${p1});`);
- code = code.replace(/getAllElement\((.+?)\)/g, (match, p1) => `document.querySelectorAll(${p1});`);
- code = code.replace(/createElement\((.+?)\)/g, (match, p1) => `document.createElement(${p1});`);
- code = code.replace(/\.addClass\((.+?)\)/g, (match, p1) => `.classList.add(${p1})`);
- code = code.replace(/([^\s].+)\.addElement\((.+?)\)/g, (match, p1, p2) => `document.${p1}.appendChild(${p2});`);
- code = code.replace(/(.+?)\.listener\((.+?)\)\:/g, (match, p1, p2) => `${p1}.addEventListener(${p2}, () => {`)
- code = code.replace(/listener\((.+?)\)\:/g, (match, p1) => `document.addEventListener(${p1}, function() {`)
-
- // change
- code = code.replace(/(.+?)\.change\((.+?), \((.+?)\)\)\:/g, (match, p1, p2, p3) => `${p1}.replace(${p2}, (${p3}) => {`)
- code = code.replace(/(.+?)\.change\((.+)\)/g, (match, p1, p2) => `${p1}.replace(${p2})`);
-
- // text
- code = code.replace(/(.+?)\.text/g, (match, p1) => `${p1}.textContent`)
- code = code.replace(/(.+?)\.text = (.+?)/g, (match, p1, p2) => `${p1}.textContent = ${p2}`);
- code = code.replace(/(? `//${p1}`);
- code = code.replace(/\*\*(.+?)/g, (match, p1) => `/*${p1}`);
- code = code.replace(/(.+?)\*\*/g, (match, p1) => `${p1}*/`);
-
- // condition property
- code = code.replace(/is_includes\s===\s([a-zA-Z0-9"'`]+)/g, (match, p1) => `includes(${p1})`);
- code = code.replace(/is_start\s===\s([a-zA-Z0-9"'`!?*#@$_&-+|.,:;=×÷%/]+)/g, (match, p1) => `startsWith(${p1})`);
- code = code.replace(/is_end\s===\s([a-zA-Z0-9"'`!?*#@$_&-+|.,:;=×÷%/]+)/g, (match, p1) => `endsWith(${p1})`);
- code = code.replace(/is_matched\s===\s"(.+?)"/g, (match, p1) => `match(${p1})`);
- code = code.replace(/is_matched\s===\s'(.+?)'/g, (match, p1) => `match(${p1})`);
- code = code.replace(/is_value\s===\s([a-zA-Z0-9"'`]+)/g, (match, p1) => `value === ${p1}`);
- code = code.replace(/is_design.(.+?)\s===\s([a-zA-Z0-9"'`]+)/g, (match, p1, p2) => `style.${p1} === ${p2}`);
- code = code.replace(/(.+?)\.design\.(.+?)\s=\s(.+?)/g, (match, p1, p2, p3) => `${p1}.style.${p2} = ${p3}`);
- code = code.replace(/(.+?)\.design\s=\s(.+?)/g, (match, p1, p2) => `${p1}.style = ${p2}`);
- code = code.replace(/is_action\s([=!><]+)\s(["'])checked(["'])/g, (match) => `checked`);
- code = code.replace(/is_action\s([=!><]+)\s(["'])selected(["'])/g, (match) => `selected`);
- code = code.replace(/is_action\s([=!><]+)\s(["'])open(["'])/g, (match) => `open`);
- code = code.replace(/is_have\s([=!><]+)\s(["'])required(["'])/g, (match) => `required`);
- code = code.replace(/is_have\s([=!><]+)\s(["'])readOnly(["'])/g, (match) => `readOnly`);
- code = code.replace(/is_have\s([=!><]+)\s(["'])autofocus(["'])/g, (match) => `autofocus`);
- code = code.replace(/is_have\s([=!><]+)\s(["'])disabled(["'])/g, (match) => `disabled`);
- code = code.replace(/is_have\s([=!><]+)\s(["'])multiple(["'])/g, (match) => `multiple`);
- code = code.replace(/is_have\s([=!><]+)\s(["'])hidden(["'])/g, (match) => `hidden`);
- code = code.replace(/delay\:\n\s+(.+?)/g, (match, p1) => `setTimeout(function() {\n ${p1}`);
-
- // loop
- code = code.replace(/repeat\:\n\s+(.+?)/g, (match, p1) => `setInterval(function() {\n ${p1}`);
- code = code.replace(/for (.+?)=(.+?) to (.+?)\:/g, (match, p1, p2, p3) => `for (let ${p1} = ${p2}; ${p1} < ${p3}; ${p1}++) {`);
- code = code.replace(/for (.+?) in (.+?)\:/g, (match, p1, p2) => `for (let ${p1} in ${p2}) {`);
- code = code.replace(/for (.+?) of (.+?)\:/g, (match, p1, p2) => `for (let ${p1} of ${p2}) {`);
- code = code.replace(/(? `switch ((${p1})) {`)
- code = code.replace("switch ((", "switch (")
- code = code.replace(")) {", ") {")
- code = code.replace(/case (.+?):\n(.+?)/g, (match, p1, p2) => `case ${p1}: \n${p2}`)
- code = code.replace(/([^\s].+?)\.image\s*=\s*(['"`][^'"`]*['"``])/g, (match, p1, p2) => `image(${p1}, ${p2})`);
- code = code.replace(/process\.argv\[(\d+)\]/g, (match, p1) => {
- var a = Number(p1) + 0;
- return `process.argv[${a}]`;
- });
- code = code.replace(/int\((.+?)\)/g, (match, p1) => `parseInt(${p1})`)
- code = code.replace(/num\((.+?)\)/g, (match, p1) => `Number(${p1})`)
- code = code.replace(/str\((.+?)\)/g, (match, p1) => `String(${p1})`)
- code = code.replace(/float\((.+?)\)/g, (match, p1) => `parseFloat(${p1})`)
- code = code.replace(/json\((.+?)\)/g, (match, p1) => `JSON.parse(${p1})`);
- code = code.replace(/(.+?)\.map\((.+?)\)\:/g, (match, p1, p2) => `${p1}.map(function(${p2}) {`)
- code = code.replace(/\.map\((.+?)\)/g, (match, p1) => `.map(${p1})`);
- code = code.replace(/(.+?)\.filter\((.+?)\)\:/g, (match, p1, p2) => `${p1}.filter(function(${p2}) {`);
- code = code.replace(/\.filter\((.+?)\)/g, (match, p1) => `.filter(${p1})`);
- code = code.replace(/(.+?)\.reduce\((.+?)\)\:/g, (match, p1, p2) => `${p1}.reduce(function(${p2}) {`)
- code = code.replace(/\.reduce\((.+?)\)/g, (match, p1) => `.reduce(${p1})`);
- code = code.replace(/(? `typeof ${p1}`)
- code = code.replace(/([a-zA-Z0-9_$]+)\.is_type\s===\s([a-zA-Z0-9"'`]+)/g, (match, p1, p2) => `typeof ${p1} === ${p2}`)
- code = code.replace(/([a-zA-Z0-9_$]+)\.is_error\s===\s(["'])type(["'])/g, (match, p1) => `${p1} instanceof TypeError`)
- code = code.replace(/([a-zA-Z0-9_$]+)\.is_error\s===\s(["'])reference(["'])/g, (match, p1) => `${p1} instanceof ReferenceError`)
- code = code.replace(/([a-zA-Z0-9_$]+)\.is_error\s===\s(["'])syntax(["'])/g, (match, p1) => `${p1} instanceof SyntaxError`)
- code = code.replace(/([a-zA-Z0-9_$]+)\.is_error\s===\s(["'])range(["'])/g, (match, p1) => `${p1} instanceof RangeError`)
- code = code.replace(/([a-zA-Z0-9_$]+)\.is_error\s===\s(["'])uri(["'])/g, (match, p1) => `${p1} instanceof URIError`)
- code = code.replace(/([a-zA-Z0-9_$]+)\.is_error\s===\s(["'])eval(["'])/g, (match, p1) => `${p1} instanceof EvalError`)
- code = code.replace(/([a-zA-Z0-9_$]+)\.is_error\s===\s(["'])(.+?)(["'])/g, (match, p1, p2, p3) => `${p1} instanceof ${p3}`)
- code = code.replace(/constructor\((.+?)\)\:/g, (match, p1) => `constructor(${p1}) {`);
- code = code.replace(/(.+?)\(\)\:/g, (match, p1) => `${p1}() {`);
- code = code.replace(/class\s(.+?)\:/g, (match, p1) => `class ${p1} {`);
- code = code.replace(/class\s(.+?)\sextends\s(.+?)\:/g, (match, p1, p2) => `class ${p1} extends ${p2} {`);
- code = code.replace(/static\((.+?)\)\ {/g, (match, p1) => `static(${p1}) {`);
- code = code.replace(/super\((.+?)\)\:/g, (match, p1) => `super(${p1})`)
- code = code.replace(/throw (.+?)/g, (match, p1) => `throw ${p1}`)
- code = code.replace(/new (.+?)/g, (match, p1) => `new ${p1}`)
- code = code.replace(/end\:(.+?)/g, (match, p1) => `}, ${p1});`);
- code = code.replace(/handle\:/g, 'try {')
- code = code.replace(/recovery\s\((.+?)\)\:/g, (match, p1) => `} catch (${p1}) {`)
- code = code.replace(/final\:/g, "} finally {")
- code = code.replace(/handle\:/g, 'try {')
- code = code.replace(/recovery\s*\((.+?)\)\:/g, (match, p1) => `} catch (${p1}) {`)
- code = code.replace(/final\:/g, "} finally {")
- code = code.replace(/Err\((.+?)\)/g, (match, p1) => `Error(${p1})`)
- code = code.replace(/URIErr\((.+?)\)/g, (match, p1) => `URIError(${p1})`)
- code = code.replace(/EvalErr\((.+?)\)/g, (match, p1) => `EvalError(${p1})`)
- code = code.replace(/TypeErr\((.+?)\)/g, (match, p1) => `TypeError(${p1})`)
- code = code.replace(/RangeErr\((.+?)\)/g, (match, p1) => `RangeError(${p1})`)
- code = code.replace(/SyntaxErr\((.+?)\)/g, (match, p1) => `Error(${p1})`)
- code = code.replace(/InternalErr\((.+?)\)/g, (match, p1) => `InternalError(${p1})`)
- code = code.replace(/ReferenceErr\((.+?)\)/g, (match, p1) => `ReferenceError(${p1})`)
-
- const script = document.createElement("script");
- script.textContent = code;
- document.body.appendChild(script);
- }
-}
-
-function image(element, imageClass) {
- if (!element) {
- console.error(`Element tidak ditemukan.`);
- return;
- }
-
- const icon = document.createElement("i");
-
- // Pisahkan class yang diberikan berdasarkan spasi
- const classes = imageClass.split(' ');
- classes.forEach(cls => icon.classList.add(cls));
-
- element.appendChild(icon);
-}
-
-function parsing(variable) {
- const patterns = [
- /let\s*(.+?)=(.+?)/g,
- /function\s*(.+?)\((.+?)\)/g,
- /if\s*\((.+?)\)\s*{/g,
- /else if\s*\((.+?)\)\s*{/g,
- /else\s*{\s*/g,
- /console(.+?)\((.+?)\)/g,
- /alert\((.+?)\)/g,
- /prompt\((.+?)\)/g,
- /Math\.random\((.+?)\)/g,
- /Math\.floor\((.+?)\)/g,
- /Math\.ceil\((.+?)\)/g,
- /Math\.min\((.+?)\)/g,
- /Math\.max\((.+?)\)/g,
- /document\.createElement\((.+?)\)/g,
- /document\.(.+?)\.appendChild\((.+?)\)/g,
- /(.+?).replace\((.+?)\)/g,
- /\/\//g,
- /(.+?)\.textContent\s*=\s*(.+?)/g,
- /\/\*(.+?)\*\//g,
- /document\.addEventListener\((.+?), function\(\) \{/g,
- /forEach\((.+?) \=\> \{/g,
- /(.+?)\.includes\((.+?)\)/g,
- /(.+?)\.match\((.+?)\)/g,
- /(.+?)\.value ===/g,
- /(.+?)\.value ==/g,
- /(.+?)\.value =/g,
- /(.+?)\.value !===/g,
- /(.+?)\.value !==/g,
- /(.+?)\.value !=/g,
- /(.+?)\.value >/g,
- /(.+?)\.value /g,
- /(.+?)\.value <==/g,
- /(.+?)\.style ===/g,
- /(.+?)\.style ==/g,
- /(.+?)\.style =/g,
- /(.+?)\.style !===/g,
- /(.+?)\.style !==/g,
- /(.+?)\.style !=/g,
- /(.+?)\.style >/g,
- /(.+?)\.style /g,
- /(.+?)\.style <==/g,
- /(.+?)\.style\.(.+?) =/g,
- /(.+?)\.style =/g,
- /(.+?)\.checked/g,
- /(.+?)\.required/g,
- /setTimeout\(function\(\) \{/g,
- /setInterval\(function\(\) \{/g,
- /for \((.+?)\) \{/g,
- /classList\.add\((.+?)\)/g,
- /switch\s*\((.+?)\)/g,
- /image\((.+?), (.+?)\)/g,
- /class\s*(.+?)\s*\{/g,
- /class\s*(.+?)\s*extends\s*(.+?)\s*\{/g,
- /constructor(.+?)\s*\{/g,
- /(.+?)\(\)\s*\{/g,
- /static(.+?)\s*\{/g,
- /typeof\s*(.+?)\s*(.+?)\s*(.+?)/g,
- /\.map\(function\((.+?)\)\s*\{/g,
- /\.filter\(function\((.+?)\)\s*\{/g,
- /\.reduce\(function\((.+?)\)\s*\{/g,
- /try\s*{/g,
- /}\s*catch\s*(.+?)\s*{/g,
- /}\s*finally\s*{/g,
- ];
-
- if (allowJs === "false") {
- const matches = patterns.map(pattern => {
- const match = variable.match(pattern);
- return match ? match[0] : null;
- }).filter(Boolean);
-
- if (matches.length > 0) {
- parsingMsg = "Invalid function '" + matches.join("', '") + "'";
- const error = new Error(parsingMsg);
- console.error(error);
- return "false";
- } else {
- parsingMsg = "";
- return "true";
- }
- }
-}
-
-function parseCode(code) {
- const lines = code.split('\n');
- const output = [];
- const stack = [];
-
- const regex = {
- listener: /(.+?)\.listener/,
- classFunc: /(.+?)\(\)\:/,
- change: /(.+?)\.change\((.+?), \((.+?)\)\)\:/,
- map: /(.+?)\.map\((.+?)\)\:/,
- filter: /(.+?)\.filter\((.+?)\)\:/,
- forEach: /forEach\((.+?)\)\:/,
- func: /func\s*(.+?)\((.+?)\)/,
- blockStart: /^(if|elif|else|class|constructor|static|final|recovery|func)/,
- };
-
- for (let line of lines) {
- const trimmedLine = line.trim();
-
- if (regex.blockStart.test(trimmedLine)) {
- stack.push('block');
- output.push(line);
- } else if (regex.listener.test(trimmedLine) || regex.change.test(trimmedLine) ||
- regex.map.test(trimmedLine) || regex.filter.test(trimmedLine) || regex.forEach.test(trimmedLine)) {
- stack.push('listener');
- output.push(line);
- } else if (regex.func.test(trimmedLine)) {
- stack.push('func');
- output.push(line);
- } else if (trimmedLine === 'end') {
- const indent = line.match(/^\s*/)[0];
- const lastBlock = stack.pop();
- output.push(lastBlock === 'listener' ? `${indent}});` : `${indent}}`);
- } else {
- output.push(line);
- }
- }
-
- return output.join('\n');
-}
-
-function addSemicolons(code) {
- const lines = code.split('\n');
- const output = [];
-
- const regex = {
- needsSemicolon: /^(var|const|return|throw|break|continue|.*\=.*|[^;{}]+\))$/,
- alreadyTerminated: /[:;{}]\s*$/
- };
-
- for (let line of lines) {
- const trimmedLine = line.trim();
-
- if (regex.needsSemicolon.test(trimmedLine) && !regex.alreadyTerminated.test(trimmedLine)) {
- output.push(line + ';');
- } else {
- output.push(line);
- }
+ const source = sourcePath
+ ? await fetch(sourcePath).then(response => {
+ if (!response.ok) throw new Error(`Failed to fetch ${sourcePath}: ${response.status}`)
+ return response.text()
+ })
+ : tag.textContent
+ const script = document.createElement('script')
+ script.textContent = compileSource(source, tag.getAttribute('allowJs') === 'true')
+ document.body.appendChild(script)
+ tag.remove()
+ } catch (error) {
+ console.error('Error executing BynixScript code:', error)
}
-
- return output.join('\n');
-}
+ })
+})
diff --git a/js/bsc.js b/js/bsc.js
index 87ab6a0..efc0902 100644
--- a/js/bsc.js
+++ b/js/bsc.js
@@ -1,39 +1 @@
-const fs = require('fs');
-
-const defaultConfig = {
- extension: {
- primary: '.bys',
- secondary: '.bynixscript',
- module: '.mbs'
- },
- readFolder: './',
- toFolder: './',
- watch: false,
- allowJs: false,
- strict: false,
- translate: 'javascript'
-};
-
-let config;
-
-try {
- const data = fs.readFileSync('./bsc.json', 'utf8');
- config = JSON.parse(data);
-} catch (err) {
- config = defaultConfig;
-}
-
-const extensions = {
- primary: config.extension?.primary || defaultConfig.extension.primary,
- secondary: config.extension?.secondary || defaultConfig.extension.secondary,
- module: config.extension?.module || defaultConfig.extension.module
-};
-
-const readFolder = config.readFolder || defaultConfig.readFolder;
-const toFolder = config.toFolder || defaultConfig.toFolder;
-const watch = config.watch || defaultConfig.watch;
-const allowJs = config.allowJs || defaultConfig.allowJs;
-const strict = config.strict || defaultConfig.strict;
-const translate = config.translate || defaultConfig.translate;
-
-module.exports = { defaultConfig, config, extensions, toFolder, readFolder, watch, allowJs, strict, translate };
+module.exports = require('../dist/modules/config.js').loadConfig('./bsc.json', '.bys')
diff --git a/js/bsconfig.js b/js/bsconfig.js
index ad6211c..737dbd7 100644
--- a/js/bsconfig.js
+++ b/js/bsconfig.js
@@ -1,39 +1 @@
-const fs = require('fs');
-
-const defaultConfig = {
- extension: {
- primary: '.bs',
- secondary: '.bynixscript',
- module: '.mbs'
- },
- readFolder: './',
- toFolder: './',
- watch: false,
- allowJs: false,
- strict: false,
- translate: 'javascript'
-};
-
-let config;
-
-try {
- const data = fs.readFileSync('./bsconfig.json', 'utf8');
- config = JSON.parse(data);
-} catch (err) {
- config = defaultConfig;
-}
-
-const extensions = {
- primary: config.extension?.primary || defaultConfig.extension.primary,
- secondary: config.extension?.secondary || defaultConfig.extension.secondary,
- module: config.extension?.module || defaultConfig.extension.module
-};
-
-const readFolder = config.readFolder || defaultConfig.readFolder;
-const toFolder = config.toFolder || defaultConfig.toFolder;
-const watch = config.watch || defaultConfig.watch;
-const allowJs = config.allowJs || defaultConfig.allowJs;
-const strict = config.strict || defaultConfig.strict;
-const translate = config.translate || defaultConfig.translate;
-
-module.exports = { defaultConfig, config, extensions, toFolder, readFolder, watch, allowJs, strict, translate };
+module.exports = require('../dist/modules/config.js').loadConfig('./bsconfig.json', '.bs')
diff --git a/js/dirProcess.js b/js/dirProcess.js
deleted file mode 100644
index a05a4bf..0000000
--- a/js/dirProcess.js
+++ /dev/null
@@ -1,47 +0,0 @@
-const fs = require('fs');
-const path = require('path');
-const vm = require('vm');
-const { translate } = require('./parser.js');
-
-function readAndProcessFilesInDirectory(directory) {
- // Baca semua file dalam direktori
- fs.readdir(directory, (err, files) => {
- if (err) {
- console.error("Error reading directory:", err);
- return;
- }
-
- // Filter hanya file .bs
- const bsFiles = files.filter(file => path.extname(file) === '.bs');
-
- // Proses setiap file .bs
- bsFiles.forEach(file => {
- const filePath = path.join(directory, file);
-
- // Baca isi file
- fs.readFile(filePath, 'utf8', (err, data) => {
- if (err) {
- console.error(`Error reading file ${file}:`, err);
- return;
- }
-
- // Konversi "print" menjadi "console.log"
- let processedData = translate(code);
-
- // Eksekusi kode yang telah diproses menggunakan vm
- try {
- const script = new vm.Script(processedData);
- script.runInThisContext();
- } catch (err) {
- console.error(`Error executing file ${file}:`, err);
- }
- });
- });
- });
-}
-
-// Panggil fungsi ini dengan direktori yang berisi file .bs
-readAndProcessFilesInDirectory('./src');
-
-// Ekspor fungsi jika dibutuhkan untuk digunakan di file lain
-module.exports = { readAndProcessFilesInDirectory };
\ No newline at end of file
diff --git a/js/download.js b/js/download.js
deleted file mode 100644
index 36749df..0000000
--- a/js/download.js
+++ /dev/null
@@ -1,22 +0,0 @@
-const https = require('https');
-
-function getDownloads(packageName, time) {
- const url = `https://api.npmjs.org/downloads/point/last-${time}/${packageName}`;
-
- https.get(url, (res) => {
- let data = '';
-
- res.on('data', (chunk) => {
- data += chunk;
- });
-
- res.on('end', () => {
- const jsonData = JSON.parse(data);
- console.log(`Package ${packageName} downloaded ${jsonData.downloads} times.`);
- });
- }).on('error', (e) => {
- console.error(`Error: ${e.message}`);
- });
-}
-
-module.exports = { https, getDownloads };
diff --git a/js/error.js b/js/error.js
deleted file mode 100644
index 018b5ee..0000000
--- a/js/error.js
+++ /dev/null
@@ -1,58 +0,0 @@
-function stackParsing(filename, errorMsg, errorStack, code, type, modulLocation) {
- if (code !== undefined && code !== null) {
- const stackLines = errorStack.split('\n');
- let codeLines = code.split('\n');
-
- const errorLine = stackLines[0];
- const parts = errorLine.split(':');
- let line;
- let column
- let tabs;
- let arrow;
- let errorCode;
- if (type === "SyntaxError") {
- const errorLine = stackLines[0];
- const parts = errorLine.split(':');
- line = parseInt(parts[1].trim());
- let l = line - 2;
- errorCode = codeLines[l].trim();
-
- return `${type}: ${errorMsg}\n at file ${filename}\n\nSnippet:\n${errorCode}\n`;
- } else if (type === "TypeError" || type === "ReferenceError" || type === "RangeError" || type === "URIError" || type === "EvalError") {
- const errorLine = stackLines[5];
- const parts = errorLine.split(':');
- line = parseInt(parts[1].trim());
- column = parseInt(parts[2].trim());
-
- if (column > 6) {
- column -= 6;
- }
-
- let l = line - 1;
- const codeNoTrim = codeLines[l]
- errorCode = codeLines[l].trim();
- const spaces = codeNoTrim.match(/^ +/);
- const spacesCount = spaces ? spaces[0].length : 0;
- let x = column - spacesCount - 1;
- if (x < 0) {
- x = Math.abs(x);
- x -= 2;
- }
- tabs = ' '.repeat(x);
- arrow = tabs + '^';
-
- return `${type}: ${errorMsg}\n at file ${filename}\n at line ${line}, column ${column}\n\nSnippet:\n${errorCode}\n${arrow}`;
- } else {
- let commonErr = errorMsg
- commonErr = commonErr.replace(/\sstack/g, '');
- commonErr = commonErr.replace(/bst\.js/g, filename);
- commonErr = commonErr.replace(/bsr\.js/g, filename);
- commonErr = commonErr.replace(/bsp\.js/g, filename);
- return `${type}: ${commonErr}`;
- }
- } else {
- return errorMsg;
- }
-}
-
-module.exports = { stackParsing };
\ No newline at end of file
diff --git a/js/watcher.js b/js/watcher.js
deleted file mode 100644
index 4f46d89..0000000
--- a/js/watcher.js
+++ /dev/null
@@ -1,93 +0,0 @@
-const chokidar = require('chokidar');
-const fs = require('fs');
-const path = require('path');
-const { parsing, parsingMsg, parseCode } = require('./parser.js')
-const { funcReplace, condReplace, forEachReplace, reassignReplace, assignReplace, logReplace, interactReplace, mathReplace, domReplace, commentReplace, utilityReplace, asyncReplace, flowReplace, forReplace, convReplace, checkReplace, oopReplace, excepReplace, otherReplace } = require('../raw/parser/parsingDecl.js')
-
-function watching(directory, directory2) {
- let code;
-
- const watcher = chokidar.watch(directory, {
- ignored: /(^|[\/\\])\../,
- persistent: true
- });
-
- watcher
- .on('add', filePath => {
- code = fs.readFileSync(filePath, 'utf8');
- code = parseCode(code);
- const parsingResults = parsing(code);
- if (parsingResults === false) {
- console.log("Please check your code and try again.")
- } else {
- code = flowReplace(code)
- code = utilityReplace(code)
- code = funcReplace(code)
- code = condReplace(code)
- code = forEachReplace(code)
- code = reassignReplace(code)
- code = assignReplace(code)
- code = logReplace(code)
- code = interactReplace(code)
- code = mathReplace(code)
- code = commentReplace(code)
- code = asyncReplace(code)
- code = forReplace(code)
- code = convReplace(code)
- code = checkReplace(code)
- code = oopReplace(code)
- code = excepReplace(code)
- code = domReplace(code)
- var newFilePath = path.join(directory2, path.basename(filePath));
- newFilePath = newFilePath.replace(".bs", ".js");
- fs.writeFileSync(newFilePath, code);
-
- console.log(`File ${newFilePath} has been created.`);
- }
- })
- .on('change', filePath => {
- code = fs.readFileSync(filePath, 'utf8');
- code = parseCode(code);
- const parsingResults = parsing(code);
- if (parsingResults === false) {
- console.log("Please check your code and try again.")
- } else {
- code = flowReplace(code)
- code = utilityReplace(code)
- code = funcReplace(code)
- code = condReplace(code)
- code = forEachReplace(code)
- code = reassignReplace(code)
- code = assignReplace(code)
- code = logReplace(code)
- code = interactReplace(code)
- code = mathReplace(code)
- code = commentReplace(code)
- code = asyncReplace(code)
- code = forReplace(code)
- code = convReplace(code)
- code = checkReplace(code)
- code = oopReplace(code)
- code = excepReplace(code)
- code = domReplace(code)
- var newFilePath = path.join(directory2, path.basename(filePath));
- newFilePath = newFilePath.replace(".bs", ".js");
- fs.writeFileSync(newFilePath, code);
-
- console.log(`File ${newFilePath} has been changed.`);
- }
- })
- .on('unlink', filePath => {
- var newFilePath = path.join(directory2, path.basename(filePath));
- newFilePath = newFilePath.replace(".bs", ".js");
- fs.unlinkSync(newFilePath);
-
- console.log(`File ${newFilePath} has been deleted.`);
- });
-
- watcher
- .on('error', error => console.log(`Watcher error: ${error}`))
- .on('ready', () => console.log('Initial scan complete. Ready for changes'));
-}
-
-module.exports = { watching };
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index caea6c2..5b2d4cb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,570 +1,587 @@
{
- "name": "bynixscript",
- "version": "0.2.1-next",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "bynixscript",
- "version": "0.2.1-next",
- "license": "MIT",
- "dependencies": {
- "chokidar": "^4.0.0",
- "commander": "^14.0.3",
- "fs": "^0.0.1-security",
- "path": "^0.4.9",
- "vm": "^0.1.0"
- },
- "bin": {
- "bs": "index.js",
- "bsd": "dist/bsd.js",
- "bsp": "dist/bsp.js",
- "bsr": "dist/bsr.js",
- "bst": "dist/bst.js",
- "bynixscript": "index.js"
- },
- "devDependencies": {
- "esbuild": "^0.25.10"
- }
- },
- "node_modules/@esbuild/aix-ppc64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
- "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-arm": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
- "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
- "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
- "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/darwin-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
- "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/darwin-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
- "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/freebsd-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
- "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/freebsd-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
- "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-arm": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
- "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
- "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-ia32": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
- "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
- "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-mips64el": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
- "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-ppc64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
- "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-riscv64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
- "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-s390x": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
- "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
- "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/netbsd-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
- "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/netbsd-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
- "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openbsd-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
- "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openbsd-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
- "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openharmony-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
- "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openharmony"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/sunos-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
- "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
- "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-ia32": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
- "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
- "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/chokidar": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
- "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
- "license": "MIT",
- "dependencies": {
- "readdirp": "^4.0.1"
- },
- "engines": {
- "node": ">= 14.16.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- }
- },
- "node_modules/commander": {
- "version": "14.0.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
- "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
- "license": "MIT",
- "engines": {
- "node": ">=20"
- }
- },
- "node_modules/esbuild": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
- "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.25.12",
- "@esbuild/android-arm": "0.25.12",
- "@esbuild/android-arm64": "0.25.12",
- "@esbuild/android-x64": "0.25.12",
- "@esbuild/darwin-arm64": "0.25.12",
- "@esbuild/darwin-x64": "0.25.12",
- "@esbuild/freebsd-arm64": "0.25.12",
- "@esbuild/freebsd-x64": "0.25.12",
- "@esbuild/linux-arm": "0.25.12",
- "@esbuild/linux-arm64": "0.25.12",
- "@esbuild/linux-ia32": "0.25.12",
- "@esbuild/linux-loong64": "0.25.12",
- "@esbuild/linux-mips64el": "0.25.12",
- "@esbuild/linux-ppc64": "0.25.12",
- "@esbuild/linux-riscv64": "0.25.12",
- "@esbuild/linux-s390x": "0.25.12",
- "@esbuild/linux-x64": "0.25.12",
- "@esbuild/netbsd-arm64": "0.25.12",
- "@esbuild/netbsd-x64": "0.25.12",
- "@esbuild/openbsd-arm64": "0.25.12",
- "@esbuild/openbsd-x64": "0.25.12",
- "@esbuild/openharmony-arm64": "0.25.12",
- "@esbuild/sunos-x64": "0.25.12",
- "@esbuild/win32-arm64": "0.25.12",
- "@esbuild/win32-ia32": "0.25.12",
- "@esbuild/win32-x64": "0.25.12"
- }
- },
- "node_modules/fs": {
- "version": "0.0.1-security",
- "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
- "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==",
- "license": "ISC"
- },
- "node_modules/path": {
- "version": "0.4.10",
- "resolved": "https://registry.npmjs.org/path/-/path-0.4.10.tgz",
- "integrity": "sha512-OXGpluyRVdUQP5fOGYMsPQo9eba09SmxaFMpM+h61gp1Mz55vhBVJt/Q3cIxAUQ17sIJn7KSwZ2cbmwRRqS1Rw==",
- "license": "MIT"
- },
- "node_modules/readdirp": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
- "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
- "license": "MIT",
- "engines": {
- "node": ">= 14.18.0"
- },
- "funding": {
- "type": "individual",
- "url": "https://paulmillr.com/funding/"
- }
- },
- "node_modules/vm": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/vm/-/vm-0.1.0.tgz",
- "integrity": "sha512-1aKVjgohVDnVhGrJLhl2k8zIrapH+7HsdnIjGvBp3XX2OCj6XGzsIbDp9rZ3r7t6qgDfXEE1EoEAEOLJm9LKnw==",
- "license": "MIT"
- }
+ "name": "bynixscript",
+ "version": "0.2.2-next",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "bynixscript",
+ "version": "0.2.2-next",
+ "license": "MIT",
+ "dependencies": {
+ "chokidar": "^4.0.0",
+ "commander": "^14.0.3",
+ "fs": "^0.0.1-security",
+ "path": "^0.4.9",
+ "vm": "^0.1.0"
+ },
+ "bin": {
+ "bynix": "dist/index.min.cjs"
+ },
+ "devDependencies": {
+ "bynixscript": "^0.2.1-next",
+ "esbuild": "^0.25.10"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/bynixscript": {
+ "version": "0.2.1-next",
+ "resolved": "https://registry.npmjs.org/bynixscript/-/bynixscript-0.2.1-next.tgz",
+ "integrity": "sha512-jsWvS2zb/F2smSGFKXd0BPZLmxf4FjBkn/KeaYOAOH5/sRLGwM22D3arYfwlIVt0FS0oBC0RGYx3Eb7KtPdhAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chokidar": "^4.0.0",
+ "fs": "^0.0.1-security",
+ "path": "^0.4.9",
+ "vm": "^0.1.0"
+ },
+ "bin": {
+ "bs": "index.js",
+ "bsd": "dist/bsd.js",
+ "bsp": "dist/bsp.js",
+ "bsr": "dist/bsr.js",
+ "bst": "dist/bst.js",
+ "bynixscript": "index.js"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/commander": {
+ "version": "14.0.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
+ "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "node_modules/fs": {
+ "version": "0.0.1-security",
+ "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
+ "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==",
+ "license": "ISC"
+ },
+ "node_modules/path": {
+ "version": "0.4.10",
+ "resolved": "https://registry.npmjs.org/path/-/path-0.4.10.tgz",
+ "integrity": "sha512-OXGpluyRVdUQP5fOGYMsPQo9eba09SmxaFMpM+h61gp1Mz55vhBVJt/Q3cIxAUQ17sIJn7KSwZ2cbmwRRqS1Rw==",
+ "license": "MIT"
+ },
+ "node_modules/readdirp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+ "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.18.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/vm": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/vm/-/vm-0.1.0.tgz",
+ "integrity": "sha512-1aKVjgohVDnVhGrJLhl2k8zIrapH+7HsdnIjGvBp3XX2OCj6XGzsIbDp9rZ3r7t6qgDfXEE1EoEAEOLJm9LKnw==",
+ "license": "MIT"
}
+ }
}
diff --git a/package.json b/package.json
index b437cb3..e06ab7e 100644
--- a/package.json
+++ b/package.json
@@ -3,10 +3,12 @@
"version": "0.2.2-next",
"description": "This is new Programming Language.",
"type": "commonjs",
- "browser": "js/browser.js",
+ "browser": "dist/browser.js",
"main": "./index.js",
"scripts": {
- "test": "node index.js"
+ "build": "node scripts/build.cjs",
+ "prepack": "npm run build",
+ "test": "npm run build && node --test tests/*.test.cjs"
},
"alias": {
"test": "npm run test"
@@ -31,7 +33,8 @@
"Indonesian"
],
"devDependencies": {
+ "bynixscript": "^0.2.1-next",
"esbuild": "^0.25.10"
},
"repository": "https://github.com/UngGasStudio/BynixScript/"
-}
\ No newline at end of file
+}
diff --git a/scripts/build.cjs b/scripts/build.cjs
new file mode 100644
index 0000000..b46d3d0
--- /dev/null
+++ b/scripts/build.cjs
@@ -0,0 +1,86 @@
+const fs = require('node:fs')
+const path = require('node:path')
+const { build } = require('esbuild')
+const { compileSource } = require('./compile.cjs')
+
+const root = path.resolve(__dirname, '..')
+const temporary = fs.mkdtempSync(path.join(root, '.bynix-build-'))
+const source = path.join(root, 'src')
+const output = path.join(root, 'dist', 'index.min.cjs')
+const generated = path.join(root, 'dist', 'modules')
+const modules = ['config', 'download', 'error', 'dirProcess', 'watcher']
+
+async function main() {
+ try {
+ for (const name of fs.readdirSync(source, { withFileTypes: true })) {
+ if (name.isFile() && name.name.endsWith('.bs')) {
+ fs.writeFileSync(path.join(temporary, name.name.replace(/\.bs$/, '.js')),
+ compileSource(fs.readFileSync(path.join(source, name.name), 'utf8')))
+ }
+ }
+
+ fs.rmSync(generated, { recursive: true, force: true })
+ await build({
+ entryPoints: modules.map(name => path.join(temporary, `${name}.js`)),
+ outdir: generated,
+ platform: 'node',
+ format: 'cjs',
+ bundle: true,
+ target: 'node20',
+ plugins: [{
+ name: 'compiler',
+ setup(bundle) {
+ bundle.onResolve({ filter: /^\.\.\/scripts\/compile\.cjs$/ }, () => ({
+ path: path.join(root, 'scripts', 'compile.cjs')
+ }))
+ }
+ }]
+ })
+
+ await build({
+ entryPoints: [path.join(temporary, 'index.js')],
+ outfile: output,
+ platform: 'node',
+ format: 'cjs',
+ bundle: true,
+ minify: true,
+ target: 'node20',
+ plugins: [{
+ name: 'build-imports',
+ setup(bundle) {
+ bundle.onResolve({ filter: /^\.\.\/scripts\/compile\.cjs$/ }, () => ({
+ path: path.join(root, 'scripts', 'compile.cjs')
+ }))
+ bundle.onResolve({ filter: /^\.\.\/package\.json$/ }, () => ({
+ path: path.join(root, 'package.json')
+ }))
+ }
+ }]
+ })
+ fs.chmodSync(output, 0o755)
+ await build({
+ entryPoints: [path.join(root, 'js', 'browser.js')],
+ outfile: path.join(root, 'dist', 'browser.js'),
+ platform: 'browser',
+ format: 'iife',
+ bundle: true,
+ plugins: [{
+ name: 'browser-config',
+ setup(bundle) {
+ bundle.onResolve({ filter: /^fs$/ }, () => ({ path: 'fs', namespace: 'browser-config' }))
+ bundle.onLoad({ filter: /.*/, namespace: 'browser-config' }, () => ({
+ contents: 'module.exports = { readFileSync() { throw new Error("No config file in browser") } }',
+ loader: 'js'
+ }))
+ }
+ }]
+ })
+ } finally {
+ fs.rmSync(temporary, { recursive: true, force: true })
+ }
+}
+
+main().catch(error => {
+ console.error(error)
+ process.exitCode = 1
+})
diff --git a/scripts/compile.cjs b/scripts/compile.cjs
new file mode 100644
index 0000000..73810ed
--- /dev/null
+++ b/scripts/compile.cjs
@@ -0,0 +1,107 @@
+const { parsing, parsingMsg, parseCode, addSemicolons } = require('bynixscript/js/parser.js')
+const replacements = require('bynixscript/dist/parser/parsingDecl.js')
+
+const passes = [
+ 'flowReplace', 'utilityReplace', 'funcReplace', 'condReplace',
+ 'forEachReplace', 'reassignReplace', 'assignReplace', 'logReplace',
+ 'interactReplace', 'mathReplace', 'commentReplace', 'asyncReplace',
+ 'forReplace', 'convReplace', 'checkReplace', 'oopReplace',
+ 'excepReplace', 'domReplace'
+]
+
+function protectSource(source) {
+ let prefix = 'BYNIXPROTECTED'
+ while (source.includes(prefix)) prefix += 'X'
+ const protectedText = []
+ let position = 0
+
+ function mask(text) {
+ if (!text) return ''
+ const token = `${prefix}${protectedText.length}TOKEN`
+ protectedText.push(text)
+ return token
+ }
+
+ function quoted(quote) {
+ const start = position++
+ while (position < source.length) {
+ if (source[position] === '\\') {
+ position += 2
+ } else if (source[position++] === quote) {
+ break
+ }
+ }
+ return mask(source.slice(start, position))
+ }
+
+ function template() {
+ let start = position++
+ let output = ''
+ while (position < source.length) {
+ if (source[position] === '\\') {
+ position += 2
+ } else if (source.startsWith('${', position)) {
+ output += mask(source.slice(start, position)) + '${'
+ position += 2
+ output += code('}')
+ start = position
+ } else if (source[position++] === '`') {
+ return output + mask(source.slice(start, position))
+ }
+ }
+ return output + mask(source.slice(start, position))
+ }
+
+ function code(terminator) {
+ let output = ''
+ let depth = 0
+ while (position < source.length) {
+ const character = source[position]
+ if (character === terminator && depth === 0) {
+ position++
+ return output + character
+ }
+ if (character === '"' || character === "'") {
+ output += quoted(character)
+ } else if (character === '`') {
+ output += template()
+ } else if (source.startsWith('//', position) || (character === '#' && !source.startsWith('#!', position))) {
+ const start = position
+ while (position < source.length && source[position] !== '\n') position++
+ const text = source.slice(start, position)
+ output += mask(character === '#' ? `//${text.slice(1)}` : text)
+ } else if (source.startsWith('/*', position) || source.startsWith('**', position)) {
+ const dialect = character === '*'
+ const start = position
+ const end = source.indexOf(dialect ? '**' : '*/', position + 2)
+ position = end === -1 ? source.length : end + 2
+ const text = source.slice(start, position)
+ output += mask(dialect ? `/*${text.slice(2, -2)}*/` : text)
+ } else {
+ if (character === '{') depth++
+ if (character === '}') depth--
+ output += character
+ position++
+ }
+ }
+ return output
+ }
+
+ const masked = code()
+ const token = new RegExp(`${prefix}(\\d+)TOKEN`, 'g')
+ return { masked, restore: text => text.replace(token, (match, index) => protectedText[Number(index)]) }
+}
+
+function compileSource(source, allowJs = false) {
+ const { masked, restore } = protectSource(source)
+ let code = addSemicolons(parseCode(masked))
+ if (!allowJs && parsing(code) === false) {
+ throw new Error(parsingMsg.message)
+ }
+ for (const pass of passes) {
+ code = replacements[pass](code)
+ }
+ return restore(code)
+}
+
+module.exports = { compileSource }
diff --git a/src/bsd.bs b/src/bsd.bs
index 5a411b6..3542db6 100644
--- a/src/bsd.bs
+++ b/src/bsd.bs
@@ -1,18 +1,10 @@
-#!/usr/bin/env node
-const fs = require('fs')
-const path = require('path')
-const { defaultConfig, config, readFolder, extensions } = require('../js/bsc.js')
+const fs = require('node:fs')
+const { resolveFile } = require('./translate.js')
-func bsdCommand(locate):
- const directory = str(readFolder)
- const fileFrom = path.join(directory, locate)
-
- handle:
- fs.unlinkSync(fileFrom)
- print("Successfully deleted " + locate + " file")
- recovery (err):
- print("Error:", err.message)
- end
+func bsdCommand(file):
+ const filePath = resolveFile(file)
+ fs.unlinkSync(filePath)
+ print(`Deleted ${file}`)
end
-module.exports = { bsdCommand }
\ No newline at end of file
+module.exports = { bsdCommand }
diff --git a/src/bsp.bs b/src/bsp.bs
index b78b559..d17bcda 100644
--- a/src/bsp.bs
+++ b/src/bsp.bs
@@ -1,69 +1,8 @@
-#!/usr/bin/env node
-const fs = require('fs')
-const vm = require('vm')
-const path = require('path')
-const { extensions, toFolder, readFolder, translate } = require('../js/bsc.js')
-const { parsing, parsingMsg, parseCode, addSemicolons } = require('../js/parser.js')
-const { funcReplace, condReplace, forEachReplace, reassignReplace, assignReplace, logReplace, interactReplace, mathReplace, domReplace, commentReplace, utilityReplace, asyncReplace, flowReplace, forReplace, convReplace, checkReplace, oopReplace, excepReplace, expReplace, otherReplace } = require('./parser/parsingDecl.js')
-const { stackParsing } = require('../js/error.js')
+const { readSource, translate } = require('./translate.js')
-const sandbox = {
- console: {
- log: () => {}
- },
- require: require,
- process: process
-};
-
-func bspCommand(locate):
- const directory = str(readFolder)
- var fileFrom = path.join(directory, locate)
-
- var code
- handle:
- var box = fs.readFileSync(fileFrom, 'utf-8')
- if locate.is_end(extensions.primary) || locate.is_end(extensions.secondary) || locate.is_end(extensions.module):
- code = parseCode(box)
- code = addSemicolons(code)
- const parsingResults = parsing(code)
-
- if parsingResults === false:
- print("Error:", parsingMsg.message)
- else:
- code = flowReplace(code)
- code = utilityReplace(code)
- code = funcReplace(code)
- code = condReplace(code)
- code = forEachReplace(code)
- code = reassignReplace(code)
- code = assignReplace(code)
- code = logReplace(code)
- code = interactReplace(code)
- code = mathReplace(code)
- code = commentReplace(code)
- code = asyncReplace(code)
- code = forReplace(code)
- code = convReplace(code)
- code = checkReplace(code)
- code = oopReplace(code)
- code = excepReplace(code)
- code = domReplace(code)
-
- vm.runInNewContext(code, sandbox);
- print(code)
- end
- else:
- print(`Error: Unsupported file extension. Please use a ${extensions.primary}, ${extensions.secondary}, or ${extensions.module} file.`)
- end
- recovery (err):
- const msg = err.message
- if msg.is_includes('module'):
- print(code)
- else:
- const stackMsg = err.stack
- print(stackParsing(locate, err.message, stackMsg, box, err.name, fileFrom))
- end
- end
+func bspCommand(file):
+ const { source } = readSource(file)
+ print(translate(source))
end
-module.exports = { bspCommand }
\ No newline at end of file
+module.exports = { bspCommand }
diff --git a/src/bsr.bs b/src/bsr.bs
index 6a4e2e6..2aaae85 100644
--- a/src/bsr.bs
+++ b/src/bsr.bs
@@ -1,67 +1,12 @@
-#!/usr/bin/env node
-const fs = require('fs')
-const vm = require('vm')
-const path = require('path')
-const { defaultConfig, config, readFolder, extensions } = require('../js/bsc.js')
-const { parsing, parsingMsg, parseCode, addSemicolons } = require('../js/parser.js')
-const { funcReplace, condReplace, forEachReplace, reassignReplace, assignReplace, logReplace, interactReplace, mathReplace, domReplace, commentReplace, utilityReplace, asyncReplace, flowReplace, forReplace, convReplace, checkReplace, oopReplace, excepReplace, expReplace, otherReplace } = require('./parser/parsingDecl.js')
-const { stackParsing } = require('../js/error.js')
+const vm = require('node:vm')
+const { createRequire } = require('node:module')
+const { readSource, translate } = require('./translate.js')
-func bsrCommand(locate):
- print(locate)
- const directory = str(readFolder)
- var fileFrom = path.join(directory, locate)
-
- const sandbox = {
- require: require,
- console: console,
- process: process,
- }
-
- var code
- handle:
- var box = fs.readFileSync(fileFrom, 'utf-8')
- if locate.is_end(extensions.primary) || locate.is_end(extensions.secondary) || locate.is_end(extensions.module):
- code = parseCode(box)
- code = addSemicolons(code)
- var parsingResults = parsing(code)
-
- if parsingResults === false:
- print("Error:", parsingMsg.message)
- else:
- code = flowReplace(code)
- code = utilityReplace(code)
- code = funcReplace(code)
- code = condReplace(code)
- code = forEachReplace(code)
- code = reassignReplace(code)
- code = assignReplace(code)
- code = logReplace(code)
- code = interactReplace(code)
- code = mathReplace(code)
- code = commentReplace(code)
- code = asyncReplace(code)
- code = forReplace(code)
- code = convReplace(code)
- code = checkReplace(code)
- code = oopReplace(code)
- code = excepReplace(code)
- code = domReplace(code)
-
- vm.runInNewContext(code, sandbox, { filename: locate});
- end
- else:
- print(`Error: Unsupported file extension. Please use a ${extensions.primary}, ${extensions.secondary}, or ${extensions.module} file.`)
- end
- recovery (err):
- const msg = err.message
- if msg.is_includes('module'):
- print("Sorry, you cannot using module right now.")
- else:
- const stackMsg = err.stack
- print(stackParsing(locate, err.message, stackMsg, box, err.name, fileFrom))
- end
- end
+func bsrCommand(file):
+ const { source, filePath } = readSource(file)
+ const code = translate(source)
+ const sandbox = { console, process, require: createRequire(filePath) }
+ vm.runInNewContext(code, sandbox, { filename: filePath })
end
-module.exports = { bsrCommand };
\ No newline at end of file
+module.exports = { bsrCommand }
diff --git a/src/bst.bs b/src/bst.bs
index f44b3bb..67d01db 100644
--- a/src/bst.bs
+++ b/src/bst.bs
@@ -1,86 +1,14 @@
-#!/usr/bin/env node
-const fs = require('fs')
-const vm = require('vm')
-const path = require('path')
-const { extensions, toFolder, readFolder, watch, strict, translate } = require('../js/bsc.js')
-const { parsing, parsingMsg, parseCode, addSemicolons } = require('../js/parser.js')
-const { funcReplace, condReplace, forEachReplace, reassignReplace, assignReplace, logReplace, interactReplace, mathReplace, domReplace, commentReplace, utilityReplace, asyncReplace, flowReplace, forReplace, convReplace, checkReplace, oopReplace, excepReplace, expReplace, otherReplace } = require('./parser/parsingDecl.js')
-const { watching } = require('../js/watcher.js')
-const { stackParsing } = require('../js/error.js')
+const fs = require('node:fs')
+const path = require('node:path')
+const { readSource, translate, extensions } = require('./translate.js')
-const sandbox = {
- console: {
- log: () => {}
- },
- require: require,
- process: process
-};
-
-func bstCommand(locate):
- const directory = str(toFolder)
- const directory2 = str(readFolder)
- var filePath = path.join(directory, locate)
- var fileFrom = path.join(directory2, locate)
-
- var code
- handle:
- var box = fs.readFileSync(fileFrom, 'utf-8')
- if watch === true:
- watching(directory2, directory)
- else:
- if locate.is_end(extensions.primary) || locate.is_end(extensions.secondary) || locate.is_end(extensions.module):
- code = parseCode(box)
- code = addSemicolons(code)
- const parsingResults = parsing(code)
-
- if parsingResults === false:
- print("Error:", parsingMsg.message)
- else:
- code = flowReplace(code)
- code = utilityReplace(code)
- code = funcReplace(code)
- code = condReplace(code)
- code = forEachReplace(code)
- code = reassignReplace(code)
- code = assignReplace(code)
- code = logReplace(code)
- code = interactReplace(code)
- code = mathReplace(code)
- code = commentReplace(code)
- code = asyncReplace(code)
- code = forReplace(code)
- code = convReplace(code)
- code = checkReplace(code)
- code = oopReplace(code)
- code = excepReplace(code)
- code = domReplace(code)
-
- vm.runInNewContext(code, sandbox);
- filePath = filePath.change(extensions.primary, ".js")
- filePath = filePath.change(extensions.secondary, ".js")
- filePath = filePath.change(extensions.module, ".mjs")
-
- fs.writeFileSync(filePath, code, 'utf8')
- print("Successfully compiled " + locate + " file")
- end
- else:
- print(`Error: Unsupported file extension. Please use a ${extensions.primary}, ${extensions.secondary}, or ${extensions.module} file.`)
- end
- end
- recovery (err):
- const msg = err.message
- if msg.is_includes('module'):
- filePath = filePath.change(extensions.primary, ".js")
- filePath = filePath.change(extensions.secondary, ".js")
- filePath = filePath.change(extensions.module, ".mjs")
-
- fs.writeFileSync(filePath, code, 'utf8')
- print("Successfully compiled " + locate + " file")
- else:
- const stackMsg = err.stack
- print(stackParsing(locate, err.message, stackMsg, box, err.name))
- end
- end
+func bstCommand(file):
+ const { source, filePath } = readSource(file)
+ const code = translate(source)
+ const extension = path.extname(filePath)
+ const output = filePath.slice(0, -extension.length) + (extension === extensions.module ? '.mjs' : '.js')
+ fs.writeFileSync(output, code, 'utf8')
+ print(`Compiled ${file} to ${output}`)
end
-module.exports = { bstCommand }
\ No newline at end of file
+module.exports = { bstCommand }
diff --git a/src/config.bs b/src/config.bs
new file mode 100644
index 0000000..cc869f4
--- /dev/null
+++ b/src/config.bs
@@ -0,0 +1,31 @@
+const fs = require('node:fs')
+
+func loadConfig(filename, primary):
+ const defaultConfig = {
+ extension: { primary, secondary: '.bynixscript', module: '.mbs' },
+ readFolder: './', toFolder: './', watch: false,
+ allowJs: false, strict: false, translate: 'javascript'
+ }
+ var config
+ handle:
+ config = JSON.parse(fs.readFileSync(filename, 'utf8'))
+ recovery (error):
+ config = defaultConfig
+ end
+ const extensions = {
+ primary: config.extension?.primary || defaultConfig.extension.primary,
+ secondary: config.extension?.secondary || defaultConfig.extension.secondary,
+ module: config.extension?.module || defaultConfig.extension.module
+ }
+ return {
+ defaultConfig, config, extensions,
+ readFolder: config.readFolder || defaultConfig.readFolder,
+ toFolder: config.toFolder || defaultConfig.toFolder,
+ watch: config.watch || defaultConfig.watch,
+ allowJs: config.allowJs || defaultConfig.allowJs,
+ strict: config.strict || defaultConfig.strict,
+ translate: config.translate || defaultConfig.translate
+ }
+end
+
+module.exports = { loadConfig }
diff --git a/src/dirProcess.bs b/src/dirProcess.bs
new file mode 100644
index 0000000..74ed5cf
--- /dev/null
+++ b/src/dirProcess.bs
@@ -0,0 +1,29 @@
+const fs = require('node:fs')
+const path = require('node:path')
+const vm = require('node:vm')
+const { compileSource } = require('../scripts/compile.cjs')
+
+func readAndProcessFilesInDirectory(directory):
+ fs.readdir(directory, (error, files) => {
+ if (error) {
+ console.error('Error reading directory:', error)
+ return
+ }
+ files.filter(file => path.extname(file) === '.bs').forEach(file => {
+ const filePath = path.join(directory, file)
+ fs.readFile(filePath, 'utf8', (readError, data) => {
+ if (readError) {
+ console.error(`Error reading file ${file}:`, readError)
+ return
+ }
+ handle:
+ new vm.Script(compileSource(data), { filename: filePath }).runInThisContext()
+ recovery (executionError):
+ console.error(`Error executing file ${file}:`, executionError)
+ end
+ })
+ })
+ })
+end
+
+module.exports = { readAndProcessFilesInDirectory }
diff --git a/src/download.bs b/src/download.bs
new file mode 100644
index 0000000..bd22c06
--- /dev/null
+++ b/src/download.bs
@@ -0,0 +1,15 @@
+const https = require('node:https')
+
+func getDownloads(packageName, time):
+ const url = `https://api.npmjs.org/downloads/point/last-${time}/${packageName}`
+ https.get(url, response => {
+ let data = ''
+ response.on('data', chunk => { data += chunk })
+ response.on('end', () => {
+ const jsonData = JSON.parse(data)
+ console.log(`Package ${packageName} downloaded ${jsonData.downloads} times.`)
+ })
+ }).on('error', error => { console.error(`Error: ${error.message}`) })
+end
+
+module.exports = { https, getDownloads }
diff --git a/src/error.bs b/src/error.bs
new file mode 100644
index 0000000..8f9498f
--- /dev/null
+++ b/src/error.bs
@@ -0,0 +1,34 @@
+func stackParsing(filename, errorMsg, errorStack, code, type, modulLocation):
+ if code === undefined || code === null:
+ return errorMsg
+ end
+ const stackLines = errorStack.split('\n')
+ const codeLines = code.split('\n')
+ if type === 'SyntaxError':
+ const line = parseInt(stackLines[0].split(':')[1].trim())
+ const errorCode = codeLines[line - 2].trim()
+ return `${type}: ${errorMsg}\n at file ${filename}\n\nSnippet:\n${errorCode}\n`
+ elif type === 'TypeError' || type === 'ReferenceError' || type === 'RangeError' || type === 'URIError' || type === 'EvalError':
+ const parts = stackLines[5].split(':')
+ const line = parseInt(parts[1].trim())
+ let column = parseInt(parts[2].trim())
+ if column > 6:
+ column -= 6
+ end
+ const codeNoTrim = codeLines[line - 1]
+ const errorCode = codeNoTrim.trim()
+ const spaces = codeNoTrim.match(/^ +/)
+ const spacesCount = spaces ? spaces[0].length : 0
+ let offset = column - spacesCount - 1
+ if offset < 0:
+ offset = Math.abs(offset) - 2
+ end
+ const arrow = ' '.repeat(offset) + '^'
+ return `${type}: ${errorMsg}\n at file ${filename}\n at line ${line}, column ${column}\n\nSnippet:\n${errorCode}\n${arrow}`
+ else:
+ const commonErr = errorMsg.replace(/\sstack/g, '').replace(/bst\.js|bsr\.js|bsp\.js/g, filename)
+ return `${type}: ${commonErr}`
+ end
+end
+
+module.exports = { stackParsing }
diff --git a/src/index.bs b/src/index.bs
index 473a79b..e41a652 100644
--- a/src/index.bs
+++ b/src/index.bs
@@ -1,28 +1,56 @@
#!/usr/bin/env node
const { Command } = require('commander')
+const { version } = require('../package.json')
const { bsrCommand } = require('./bsr.js')
const { bstCommand } = require('./bst.js')
const { bspCommand } = require('./bsp.js')
const { bsdCommand } = require('./bsd.js')
-const program = new Command();
+const program = new Command()
-program.name('bynix').description('BynixScript command line interface tools').version('0.2.2-next')
+program.name('bynix').description('BynixScript command line interface').version(version)
+program.showHelpAfterError()
-program.option('-r, --run ', 'running files with extensions .bys').option('-c, --compile ', 'compile .bys files to .js').option('-p, --print', 'print code to cli').option('-d, --delete', 'delete a .bys file')
+program.command('run ').description('Run a BynixScript file').action(bsrCommand)
+program.command('compile ').description('Compile a BynixScript file to JavaScript').action(bstCommand)
+program.command('print ').description('Print the translated JavaScript without running it').action(bspCommand)
+program.command('delete ').description('Delete a BynixScript file').action(bsdCommand)
-program.parse()
+program.option('-r, --run ', 'Run a file')
+program.option('-c, --compile ', 'Compile a file')
+program.option('-p, --print ', 'Print translated JavaScript')
+program.option('-d, --delete ', 'Delete a file')
+func noOp():
+end
+program.action(noOp)
-const options = program.opts()
+handle:
+ program.parse()
+ const options = program.opts()
+ const selected = []
+ for const name of ['run', 'compile', 'print', 'delete']:
+ if options[name] !== undefined:
+ selected.push(name)
+ end
+ end
-if options.run:
- bsrCommand(options.run)
-elif options.compile:
- bstCommand(options.compile)
-elif options.print:
- bspCommand(options.print)
-elif options.delete:
- bsdCommand(options.delete)
-else:
- console.log('No config provided')
-end
\ No newline at end of file
+ if selected.length > 1:
+ throw new Err('Specify only one command at a time.')
+ elif selected.length === 1:
+ const name = selected[0]
+ if name === 'run':
+ bsrCommand(options[name])
+ elif name === 'compile':
+ bstCommand(options[name])
+ elif name === 'print':
+ bspCommand(options[name])
+ else:
+ bsdCommand(options[name])
+ end
+ elif process.argv.length === 2:
+ program.error('A command is required.')
+ end
+recovery (error):
+ process.stderr.write(`Error: ${error.message}\n`)
+ process.exitCode = 1
+end
diff --git a/src/translate.bs b/src/translate.bs
new file mode 100644
index 0000000..4644bc2
--- /dev/null
+++ b/src/translate.bs
@@ -0,0 +1,32 @@
+const fs = require('node:fs')
+const path = require('node:path')
+const { extensions } = require('./config.js').loadConfig('./bsc.json', '.bys')
+const { compileSource } = require('../scripts/compile.cjs')
+
+func resolveFile(file):
+ if typeof file !== 'string' || file.trim() === '':
+ throw new Err('A file path is required.')
+ end
+ if !file.is_end(extensions.primary) && !file.is_end(extensions.secondary) && !file.is_end(extensions.module):
+ throw new Err(`Unsupported file extension: ${file}. Use ${extensions.primary}, ${extensions.secondary}, or ${extensions.module}.`)
+ end
+ const filePath = path.resolve(file)
+ if !fs.existsSync(filePath):
+ throw new Err(`File not found: ${file}`)
+ end
+ if !fs.statSync(filePath).isFile():
+ throw new Err(`Not a file: ${file}`)
+ end
+ return filePath
+end
+
+func readSource(file):
+ const filePath = resolveFile(file)
+ return { filePath, source: fs.readFileSync(filePath, 'utf8') }
+end
+
+func translate(source):
+ return compileSource(source)
+end
+
+module.exports = { resolveFile, readSource, translate, extensions }
diff --git a/src/watcher.bs b/src/watcher.bs
new file mode 100644
index 0000000..724f580
--- /dev/null
+++ b/src/watcher.bs
@@ -0,0 +1,43 @@
+const chokidar = require('chokidar')
+const fs = require('node:fs')
+const path = require('node:path')
+const { compileSource } = require('../scripts/compile.cjs')
+
+func watching(directory, directory2):
+ const watcher = chokidar.watch(directory, { ignored: /(^|[\/\\])\../, persistent: true })
+ const extensions = ['.bs', '.bys', '.bynixscript', '.mbs']
+ func outputPath(filePath):
+ const extension = path.extname(filePath)
+ if !extensions.includes(extension):
+ return null
+ end
+ return path.join(directory2, path.basename(filePath, extension) + (extension === '.mbs' ? '.mjs' : '.js'))
+ end
+ func compileFile(filePath):
+ const output = outputPath(filePath)
+ if !output:
+ return
+ end
+ handle:
+ const source = fs.readFileSync(filePath, 'utf8')
+ const code = compileSource(source)
+ fs.writeFileSync(output, code)
+ console.log(`File ${output} has been created or changed.`)
+ recovery (error):
+ console.error(`Error compiling ${filePath}:`, error)
+ end
+ end
+ func removeFile(filePath):
+ const output = outputPath(filePath)
+ if output && fs.existsSync(output):
+ fs.unlinkSync(output)
+ console.log(`File ${output} has been deleted.`)
+ end
+ end
+ watcher.on('add', compileFile).on('change', compileFile).on('unlink', removeFile)
+ watcher.on('error', error => console.error('Watcher error:', error))
+ watcher.on('ready', () => console.log('Initial scan complete. Ready for changes'))
+ return watcher
+end
+
+module.exports = { watching }
diff --git a/tests/cli.test.cjs b/tests/cli.test.cjs
new file mode 100644
index 0000000..b1f8b56
--- /dev/null
+++ b/tests/cli.test.cjs
@@ -0,0 +1,421 @@
+const test = require('node:test')
+const assert = require('node:assert/strict')
+const fs = require('node:fs')
+const os = require('node:os')
+const path = require('node:path')
+const { spawnSync } = require('node:child_process')
+const { Script, runInNewContext } = require('node:vm')
+const { compileSource } = require('../scripts/compile.cjs')
+
+const root = path.resolve(__dirname, '..')
+const binary = path.join(root, 'dist', 'index.min.cjs')
+const generated = path.join(root, 'dist', 'modules')
+
+function invoke(args, cwd = root) {
+ return spawnSync(process.execPath, [binary, ...args], { cwd, encoding: 'utf8' })
+}
+
+function fixture(context) {
+ const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'bynix-cli-'))
+ context.after(() => fs.rmSync(directory, { recursive: true, force: true }))
+ return directory
+}
+
+test('build produces the packaged executable', () => {
+ const result = spawnSync(process.execPath, [path.join(root, 'scripts', 'build.cjs')], {
+ cwd: root, encoding: 'utf8'
+ })
+ assert.equal(result.status, 0, result.stderr)
+ assert.ok(fs.statSync(binary).isFile())
+ for (const name of ['config', 'download', 'error', 'dirProcess', 'watcher']) {
+ assert.equal(fs.existsSync(path.join(root, 'js', `${name}.js`)), false)
+ assert.ok(fs.statSync(path.join(generated, `${name}.js`)).isFile())
+ }
+ assert.match(fs.readFileSync(binary, 'utf8'), /^#!\/usr\/bin\/env node/)
+ assert.equal(invoke(['--version']).status, 0)
+})
+
+test('every CLI source compiles through the build compiler', () => {
+ for (const name of ['index', 'bsr', 'bst', 'bsp', 'bsd', 'translate',
+ 'config', 'download', 'error', 'dirProcess', 'watcher']) {
+ const source = fs.readFileSync(path.join(root, 'src', `${name}.bs`), 'utf8')
+ const code = compileSource(source)
+ assert.match(source, /\bfunc \w+\(|\bhandle:/)
+ assert.doesNotMatch(source, /\bfunction\s+\w+\s*\(|\btry\s*\{/)
+ assert.match(code, /function \w+\(|try \{/)
+ assert.doesNotThrow(() => new Script(code, { filename: `${name}.js` }))
+ }
+ assert.match(compileSource('func valid(value):\n print(value)\nend'), /function valid\(value\) \{/)
+ assert.throws(() => new Script(compileSource('func invalid():\n print("x")\n')), SyntaxError)
+})
+
+test('bootstrap compiler preserves quoted and template text while translating code', () => {
+ const source = [
+ "const single = 'func print(1) is_end(\"x\") # \\'quoted\\''",
+ 'const double = "handle: print(2) rand(3) \\"quoted\\""',
+ 'const template = `first print(3) # **',
+ 'end \\`escaped\\` ${single} ${`nested print(4) ${double}`}`',
+ 'const interpolated = `print(5) ${"needle".is_includes("need")}`',
+ 'func show():',
+ ' print(single, double, template, interpolated)',
+ 'end',
+ 'show()'
+ ].join('\n')
+ const output = compileSource(source)
+ assert.match(output, /function show\(\) \{/)
+ for (const literal of [
+ "'func print(1) is_end(\"x\") # \\'quoted\\''",
+ '"handle: print(2) rand(3) \\"quoted\\""',
+ '`first print(3) # **\nend \\`escaped\\` ${single} ${`nested print(4) ${double}`}`',
+ '`print(5) ${"needle".includes("need")}`'
+ ]) {
+ assert.ok(output.includes(literal), `Missing unchanged literal: ${literal}`)
+ }
+ const values = []
+ runInNewContext(output, { console: { log: (...args) => values.push(args) } })
+ assert.deepEqual(values, [[
+ "func print(1) is_end(\"x\") # 'quoted'",
+ 'handle: print(2) rand(3) "quoted"',
+ 'first print(3) # **\nend `escaped` func print(1) is_end("x") # \'quoted\' nested print(4) handle: print(2) rand(3) "quoted"',
+ 'print(5) true'
+ ]])
+})
+
+test('bootstrap compiler preserves line and block comments without executing their contents', () => {
+ const source = [
+ 'const events = []',
+ '# print("from hash") func hidden():',
+ '// print("from slash") is_end("x")',
+ '/* print("from block")',
+ 'end func hidden(): */',
+ '** print("from dialect block")',
+ 'end is_includes("x") **',
+ 'events.push("safe")',
+ 'print(events.join(","))'
+ ].join('\n')
+ const output = compileSource(source)
+ for (const comment of [
+ '// print("from hash") func hidden():',
+ '// print("from slash") is_end("x")',
+ '/* print("from block")\nend func hidden(): */',
+ '/* print("from dialect block")\nend is_includes("x") */'
+ ]) {
+ assert.ok(output.includes(comment), `Missing unchanged comment: ${comment}`)
+ }
+ const values = []
+ runInNewContext(output, { console: { log: value => values.push(value) } })
+ assert.deepEqual(values, ['safe'])
+})
+
+test('packaged compiler preserves literals and ignores comments', context => {
+ const directory = fixture(context)
+ const source = [
+ 'const value = `print(1) # \\`quoted\\`',
+ 'end ${"func is_end(2)"}`',
+ '# print("unsafe hash")',
+ '/* print("unsafe block")',
+ 'end */',
+ 'print(value)'
+ ].join('\n')
+ fs.writeFileSync(path.join(directory, 'protected.bys'), source)
+ const compilation = invoke(['compile', 'protected.bys'], directory)
+ assert.equal(compilation.status, 0, compilation.stderr)
+ const output = fs.readFileSync(path.join(directory, 'protected.js'), 'utf8')
+ assert.ok(output.includes('`print(1) # \\`quoted\\`\nend ${"func is_end(2)"}`'))
+ assert.ok(output.includes('// print("unsafe hash")'))
+ assert.ok(output.includes('/* print("unsafe block")\nend */'))
+ const result = invoke(['run', 'protected.bys'], directory)
+ assert.equal(result.status, 0, result.stderr)
+ assert.equal(result.stdout.trim(), 'print(1) # `quoted`\nend func is_end(2)')
+ assert.doesNotMatch(result.stdout, /unsafe/)
+})
+
+test('global and command help succeeds', () => {
+ assert.match(invoke(['--help']).stdout, /run /)
+ for (const command of ['run', 'compile', 'print', 'delete']) {
+ const result = invoke([command, '--help'])
+ assert.equal(result.status, 0, result.stderr)
+ assert.match(result.stdout, new RegExp(`bynix ${command} .*`))
+ }
+})
+
+test('run executes a source file without writing JavaScript', context => {
+ const directory = fixture(context)
+ fs.writeFileSync(path.join(directory, 'hello.bys'),
+ 'func greet():\n print("hello from run")\nend\ngreet()\n')
+ const result = invoke(['run', 'hello.bys'], directory)
+ assert.equal(result.status, 0, result.stderr)
+ assert.match(result.stdout, /hello from run/)
+ assert.equal(fs.existsSync(path.join(directory, 'hello.js')), false)
+})
+
+test('run reports runtime failures with a nonzero status', context => {
+ const directory = fixture(context)
+ fs.writeFileSync(path.join(directory, 'broken.bys'), 'missingFunction()\n')
+ const result = invoke(['run', 'broken.bys'], directory)
+ assert.notEqual(result.status, 0)
+ assert.match(result.stderr, /missingFunction is not defined/)
+})
+
+test('compile writes JavaScript without executing source code', context => {
+ const directory = fixture(context)
+ fs.writeFileSync(path.join(directory, 'hello.bynixscript'), 'print("not executed")\n')
+ const result = invoke(['compile', 'hello.bynixscript'], directory)
+ assert.equal(result.status, 0, result.stderr)
+ assert.doesNotMatch(result.stdout, /not executed/)
+ assert.match(fs.readFileSync(path.join(directory, 'hello.js'), 'utf8'), /console\.log\("not executed"\)/)
+ fs.writeFileSync(path.join(directory, 'module.mbs'), 'print("module")\n')
+ assert.equal(invoke(['compile', 'module.mbs'], directory).status, 0)
+ assert.ok(fs.existsSync(path.join(directory, 'module.mjs')))
+})
+
+test('print translates without executing or writing a file', context => {
+ const directory = fixture(context)
+ fs.writeFileSync(path.join(directory, 'hello.bys'), 'print("print only")\n')
+ const result = invoke(['print', 'hello.bys'], directory)
+ assert.equal(result.status, 0, result.stderr)
+ assert.match(result.stdout, /console\.log\("print only"\)/)
+ assert.doesNotMatch(result.stdout, /^print only$/m)
+ assert.equal(fs.existsSync(path.join(directory, 'hello.js')), false)
+})
+
+test('delete only removes the specified file', context => {
+ const directory = fixture(context)
+ fs.writeFileSync(path.join(directory, 'remove.bys'), 'print("bye")\n')
+ fs.writeFileSync(path.join(directory, 'keep.bys'), 'print("stay")\n')
+ const result = invoke(['delete', 'remove.bys'], directory)
+ assert.equal(result.status, 0, result.stderr)
+ assert.equal(fs.existsSync(path.join(directory, 'remove.bys')), false)
+ assert.ok(fs.existsSync(path.join(directory, 'keep.bys')))
+})
+
+test('missing arguments never delete a file and return failure', context => {
+ const directory = fixture(context)
+ fs.writeFileSync(path.join(directory, 'undefined.bys'), 'safe')
+ for (const command of ['run', 'compile', 'print', 'delete']) {
+ const result = invoke([command], directory)
+ assert.notEqual(result.status, 0)
+ assert.match(result.stderr, /missing required argument/)
+ }
+ assert.notEqual(invoke([], directory).status, 0)
+ assert.notEqual(invoke(['-d'], directory).status, 0)
+ assert.ok(fs.existsSync(path.join(directory, 'undefined.bys')))
+})
+
+test('invalid paths and extensions return errors for every command', context => {
+ const directory = fixture(context)
+ fs.writeFileSync(path.join(directory, 'invalid.txt'), 'safe')
+ for (const command of ['run', 'compile', 'print', 'delete']) {
+ const missing = invoke([command, 'absent.bys'], directory)
+ assert.notEqual(missing.status, 0)
+ assert.match(missing.stderr, /File not found/)
+ const extension = invoke([command, 'invalid.txt'], directory)
+ assert.notEqual(extension.status, 0)
+ assert.match(extension.stderr, /Unsupported file extension/)
+ }
+ assert.ok(fs.existsSync(path.join(directory, 'invalid.txt')))
+})
+
+test('legacy file options still work', context => {
+ const directory = fixture(context)
+ fs.writeFileSync(path.join(directory, 'legacy.bys'), 'print("legacy")\n')
+ assert.match(invoke(['-r', 'legacy.bys'], directory).stdout, /legacy/)
+ assert.match(invoke(['-p', 'legacy.bys'], directory).stdout, /console\.log/)
+ assert.equal(invoke(['-c', 'legacy.bys'], directory).status, 0)
+ assert.ok(fs.existsSync(path.join(directory, 'legacy.js')))
+ assert.equal(invoke(['-d', 'legacy.bys'], directory).status, 0)
+ assert.equal(fs.existsSync(path.join(directory, 'legacy.bys')), false)
+})
+
+test('conflicting legacy commands fail without changing files', context => {
+ const directory = fixture(context)
+ fs.writeFileSync(path.join(directory, 'safe.bys'), 'print("safe")\n')
+ const result = invoke(['-p', 'safe.bys', '-d', 'safe.bys'], directory)
+ assert.notEqual(result.status, 0)
+ assert.match(result.stderr, /Specify only one command/)
+ assert.ok(fs.existsSync(path.join(directory, 'safe.bys')))
+})
+
+test('generated modules expose their source implementations', () => {
+ const exports = {
+ config: ['loadConfig'], download: ['https', 'getDownloads'], error: ['stackParsing'],
+ dirProcess: ['readAndProcessFilesInDirectory'], watcher: ['watching']
+ }
+ for (const [name, expected] of Object.entries(exports)) {
+ assert.deepEqual(Object.keys(require(path.join(generated, `${name}.js`))), expected)
+ }
+})
+
+test('package contains the CLI, browser, and generated modules without js duplicates', () => {
+ const result = spawnSync('npm', ['pack', '--dry-run', '--ignore-scripts', '--json'], {
+ cwd: root, encoding: 'utf8'
+ })
+ assert.equal(result.status, 0, result.stderr)
+ const files = new Set(JSON.parse(result.stdout)[0].files.map(file => file.path))
+ for (const file of ['dist/index.min.cjs', 'dist/browser.js',
+ ...['config', 'download', 'error', 'dirProcess', 'watcher'].map(name => `dist/modules/${name}.js`)]) {
+ assert.ok(files.has(file), `${file} missing from package`)
+ }
+ for (const name of ['config', 'download', 'error', 'dirProcess', 'watcher']) {
+ assert.equal(files.has(`js/${name}.js`), false)
+ }
+})
+
+test('configuration wrappers preserve separate defaults and public exports', context => {
+ const directory = fixture(context)
+ const { loadConfig } = require('../dist/modules/config.js')
+ const legacy = require('../js/bsconfig.js')
+ const current = require('../js/bsc.js')
+ assert.equal(legacy.extensions.primary, '.bs')
+ assert.equal(current.extensions.primary, '.bys')
+ assert.equal(legacy.defaultConfig.extension.primary, '.bs')
+ assert.equal(current.defaultConfig.extension.primary, '.bys')
+ for (const name of ['defaultConfig', 'config', 'extensions', 'readFolder', 'toFolder',
+ 'watch', 'allowJs', 'strict', 'translate']) {
+ assert.ok(Object.hasOwn(current, name))
+ assert.ok(Object.hasOwn(legacy, name))
+ }
+ const file = path.join(directory, 'custom.json')
+ fs.writeFileSync(file, JSON.stringify({ extension: { secondary: '.custom' }, readFolder: 'input', watch: true }))
+ const loaded = loadConfig(file, '.bys')
+ assert.equal(loaded.extensions.primary, '.bys')
+ assert.equal(loaded.extensions.secondary, '.custom')
+ assert.equal(loaded.readFolder, 'input')
+ assert.equal(loaded.watch, true)
+ assert.equal(loadConfig(path.join(directory, 'missing.json'), '.bs').extensions.primary, '.bs')
+})
+
+test('generated error formatter preserves its public behavior', () => {
+ const { stackParsing } = require('../dist/modules/error.js')
+ assert.equal(stackParsing('test.bys', 'missing', '', null, 'Error'), 'missing')
+ assert.equal(stackParsing('test.bys', 'failed in bst.js', '', 'code', 'Error'),
+ 'Error: failed in test.bys')
+ assert.equal(stackParsing('test.bys', 'invalid', 'test.bys:3:1', 'first\nsecond\nthird', 'SyntaxError'),
+ 'SyntaxError: invalid\n at file test.bys\n\nSnippet:\nsecond\n')
+})
+
+test('generated download utility keeps its https export and reports counts', () => {
+ const { EventEmitter } = require('node:events')
+ const { https, getDownloads } = require('../dist/modules/download.js')
+ const originalGet = https.get
+ const originalLog = console.log
+ const messages = []
+ try {
+ console.log = message => messages.push(message)
+ https.get = (url, callback) => {
+ assert.equal(url, 'https://api.npmjs.org/downloads/point/last-week/bynixscript')
+ const response = new EventEmitter()
+ callback(response)
+ response.emit('data', '{"downloads":42}')
+ response.emit('end')
+ return new EventEmitter()
+ }
+ getDownloads('bynixscript', 'week')
+ assert.deepEqual(messages, ['Package bynixscript downloaded 42 times.'])
+ } finally {
+ https.get = originalGet
+ console.log = originalLog
+ }
+})
+
+test('directory processor does not run on import and compiles file contents', async context => {
+ const directory = fixture(context)
+ fs.writeFileSync(path.join(directory, 'example.bs'), 'globalThis.bynixDirectoryTest = "print(1)"\n# print("unsafe")\n')
+ const modulePath = path.join(generated, 'dirProcess.js')
+ const importOnly = spawnSync(process.execPath, ['-e', `require(${JSON.stringify(modulePath)})`], {
+ cwd: directory, encoding: 'utf8'
+ })
+ assert.equal(importOnly.status, 0, importOnly.stderr)
+ assert.equal(importOnly.stdout, '')
+ assert.equal(importOnly.stderr, '')
+ const { readAndProcessFilesInDirectory } = require(modulePath)
+ context.after(() => { delete globalThis.bynixDirectoryTest })
+ readAndProcessFilesInDirectory(directory)
+ for (let attempt = 0; attempt < 100 && globalThis.bynixDirectoryTest === undefined; attempt++) {
+ await new Promise(resolve => setTimeout(resolve, 10))
+ }
+ assert.equal(globalThis.bynixDirectoryTest, 'print(1)')
+})
+
+test('watcher compiles with the shared compiler and removes generated output', async context => {
+ const source = fixture(context)
+ const output = fixture(context)
+ const { watching } = require('../dist/modules/watcher.js')
+ const watcher = watching(source, output)
+ context.after(() => watcher.close())
+ await new Promise((resolve, reject) => {
+ watcher.once('ready', resolve)
+ watcher.once('error', reject)
+ })
+ const file = path.join(source, 'watched.bys')
+ const generated = path.join(output, 'watched.js')
+ fs.writeFileSync(file, 'print("watch")\n# print("not executed")\n')
+ async function waitUntil(predicate) {
+ for (let attempt = 0; attempt < 100; attempt++) {
+ if (predicate()) return
+ await new Promise(resolve => setTimeout(resolve, 20))
+ }
+ assert.fail('watcher did not update the output')
+ }
+ await waitUntil(() => fs.existsSync(generated))
+ assert.equal(fs.readFileSync(generated, 'utf8'), compileSource(fs.readFileSync(file, 'utf8')))
+ fs.unlinkSync(file)
+ await waitUntil(() => !fs.existsSync(generated))
+})
+
+test('browser bundle runs the shared compiler without changing strings or comments', async () => {
+ const browser = fs.readFileSync(path.join(root, 'dist', 'browser.js'), 'utf8')
+ const scripts = []
+ let onReady
+ let removed = false
+ const tag = {
+ textContent: 'const text = "print(123)"\n# print("unsafe")\nprint(text)',
+ getAttribute: () => null,
+ remove: () => { removed = true }
+ }
+ const document = {
+ createElement: () => ({ classList: { add() {} } }),
+ head: { appendChild() {} },
+ body: { appendChild: script => scripts.push(script.textContent) },
+ addEventListener: (name, callback) => { onReady = callback },
+ querySelectorAll: () => [tag]
+ }
+ runInNewContext(browser, { document, console })
+ onReady()
+ await new Promise(resolve => setImmediate(resolve))
+ assert.equal(removed, true)
+ assert.equal(scripts.length, 1)
+ assert.ok(scripts[0].includes('"print(123)"'))
+ assert.ok(scripts[0].includes('// print("unsafe")'))
+ const printed = []
+ runInNewContext(scripts[0], { console: { log: value => printed.push(value) } })
+ assert.deepEqual(printed, ['print(123)'])
+})
+
+test('browser bundle fetches sources and honors allowJs', async () => {
+ const browser = fs.readFileSync(path.join(root, 'dist', 'browser.js'), 'utf8')
+ const scripts = []
+ let onReady
+ const tag = {
+ getAttribute: name => ({ src: 'remote.bys', allowJs: 'true' })[name] ?? null,
+ remove() {}
+ }
+ const document = {
+ createElement: () => ({ classList: { add() {} } }),
+ head: { appendChild() {} },
+ body: { appendChild: script => scripts.push(script.textContent) },
+ addEventListener: (name, callback) => { onReady = callback },
+ querySelectorAll: () => [tag]
+ }
+ runInNewContext(browser, {
+ document, console,
+ fetch: async file => {
+ assert.equal(file, 'remote.bys')
+ return { ok: true, text: async () => 'const message = "print(1)"\nprint(message)\n' }
+ }
+ })
+ onReady()
+ await new Promise(resolve => setImmediate(resolve))
+ assert.equal(scripts.length, 1)
+ assert.ok(scripts[0].includes('"print(1)"'))
+})