neo-junny-framework 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # NeoVGC 사용법
2
+
3
+ 1. `.neo` 파일을 작성합니다.
4
+ 2. `npx neoc src/App.neo`를 실행하여 컴파일합니다.
5
+ 3. 생성된 `App.js`를 HTML에서 불러옵니다.
@@ -0,0 +1,54 @@
1
+ export class NeoParser {
2
+ static parse(code) {
3
+ // 1. @Script 블록 추출 로직 개선
4
+ // 괄호를 포함해서 캡처한 뒤, 앞뒤 껍데기만 날리는 방식이 가장 안전합니다.
5
+ const scriptMatch = code.match(/@Script\s*\{([\s\S]*?)\}(?=\s*@|$)/);
6
+ let scriptContent = "";
7
+
8
+ if (scriptMatch) {
9
+ scriptContent = scriptMatch[1].trim();
10
+ // 만약 사용자가 적은 마지막 } 가 잘려 나갔다면 복구하거나,
11
+ // 원본에서 블록 전체를 가져오는 로직으로 보강
12
+ }
13
+
14
+ // 2. UI 요소 추출 (이 부분은 동일)
15
+ const uiCode = code.replace(/@Script\s*\{[\s\S]*?\}\s*/, "");
16
+ const tokenRegex = /(@\w+:\w+)|(Innerhtml:\s*".*?")|(Style\([\s\S]*?\))|(Event\([\s\S]*?\))|(\{)|(\})/g;
17
+ const tokens = uiCode.match(tokenRegex);
18
+
19
+ const stack = [];
20
+ let root = null;
21
+
22
+ tokens.forEach(token => {
23
+ if (token.startsWith('@')) {
24
+ const [_, id, tag] = token.match(/@(\w+):(\w+)/);
25
+ const node = { id, tag, innerHtml: "", styles: {}, events: {}, children: [] };
26
+ if (stack.length > 0) stack[stack.length - 1].children.push(node);
27
+ else if (!root) root = node;
28
+ stack.push(node);
29
+ } else if (token === '}') {
30
+ stack.pop();
31
+ } else if (token.startsWith('Innerhtml')) {
32
+ stack[stack.length - 1].innerHtml = token.match(/"(.*?)"/)[1];
33
+ } else if (token.startsWith('Style')) {
34
+ stack[stack.length - 1].styles = this.parseKV(token.match(/Style\((.*?)\)/)[1]);
35
+ } else if (token.startsWith('Event')) {
36
+ stack[stack.length - 1].events = this.parseKV(token.match(/Event\((.*?)\)/)[1]);
37
+ }
38
+ });
39
+
40
+ return { root, scriptContent };
41
+ }
42
+
43
+ static parseKV(str) {
44
+ const obj = {};
45
+ str.split(';').forEach(line => {
46
+ const [k, v] = line.split(':').map(s => s?.trim());
47
+ if (k && v) {
48
+ const camelK = k.replace(/-([a-z])/g, g => g[1].toUpperCase());
49
+ obj[camelK] = v;
50
+ }
51
+ });
52
+ return obj;
53
+ }
54
+ }
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs';
3
+ import { NeoParser } from './NeoParser.js';
4
+
5
+ const inputFile = process.argv[2];
6
+ const source = fs.readFileSync(inputFile, 'utf8');
7
+ const { root, scriptContent } = NeoParser.parse(source);
8
+
9
+ function generateCode(node) {
10
+ const childrenCode = node.children.map(child => generateCode(child)).join(', ');
11
+
12
+ const eventProps = {};
13
+ for (const [evt, action] of Object.entries(node.events)) {
14
+ const propName = `on${evt.charAt(0).toUpperCase() + evt.slice(1)}`;
15
+
16
+ // 로직 개선: 세미콜론이나 오타로 잘린 부분이 있는지 체크하고 보정
17
+ let processedAction = action;
18
+ if (action.includes('++')) processedAction = `state.${action}`;
19
+
20
+ // 만약 action 끝에 )가 없는데 (는 있다면 붙여주기 (방어코드)
21
+ if (processedAction.includes('(') && !processedAction.includes(')')) {
22
+ processedAction += ')';
23
+ }
24
+
25
+ eventProps[propName] = `() => { ${processedAction} }`; // 여기서 세미콜론은 자동으로 붙지 않으니 주의
26
+ }
27
+
28
+ const eventString = Object.entries(eventProps)
29
+ .map(([k, v]) => `${k}: ${v}`)
30
+ .join(', ');
31
+
32
+ return `h('${node.tag}', {
33
+ id: '${node.id}',
34
+ style: ${JSON.stringify(node.styles)},
35
+ innerHtml: \`${node.innerHtml.replace(/\$(\w+)/g, '${state.$1}')}\`${eventString ? ', ' + eventString : ''}
36
+ }, [${childrenCode}])`;
37
+ }
38
+
39
+ const finalJS = `
40
+ import { h } from '../core/NeoCore.js';
41
+
42
+ // [User Script]
43
+ ${scriptContent}
44
+ // 만약 사용자가 실수로 }를 안 닫았을 경우를 대비해 한 줄 띄움
45
+
46
+ export default function render(state) {
47
+ return ${generateCode(root)};
48
+ }
49
+ `.trim();
50
+
51
+ fs.writeFileSync(inputFile.replace('.neo', '.js'), finalJS);
52
+ console.log("✅ [Script & Event] 컴파일 완료!");
@@ -0,0 +1,52 @@
1
+ export class NeoCore {
2
+ constructor(state, rootRenderFn, containerId) {
3
+ this.container = document.getElementById(containerId);
4
+ this.rootRenderFn = rootRenderFn;
5
+ this.state = new Proxy(state, {
6
+ set: (target, key, value) => {
7
+ target[key] = value;
8
+ this.mount(); // 상태 변화 시 리렌더링
9
+ return true;
10
+ }
11
+ });
12
+ }
13
+
14
+ mount() {
15
+ if (!this.container) return;
16
+ this.container.innerHTML = '';
17
+ const domTree = this.rootRenderFn(this.state);
18
+ this.container.appendChild(domTree);
19
+ }
20
+ }
21
+
22
+ // 가상 노드를 DOM 요소로 바꾸는 함수
23
+ export function h(tag, props, children = []) {
24
+ const el = document.createElement(tag);
25
+
26
+ // 1. ID 설정
27
+ if (props.id) el.id = props.id;
28
+
29
+ // 2. 스타일 설정
30
+ if (props.style) Object.assign(el.style, props.style);
31
+
32
+ // 3. 내용물 설정
33
+ if (props.innerHtml) el.innerHTML = props.innerHtml;
34
+
35
+ // 💡 4. 이벤트 연결 (이 부분이 빠져있을 거예요!)
36
+ // props에 on으로 시작하는 속성(onClick 등)이 있다면 이벤트를 등록합니다.
37
+ Object.keys(props).forEach(key => {
38
+ if (key.startsWith('on') && typeof props[key] === 'function') {
39
+ const eventType = key.toLowerCase().substring(2); // 'onClick' -> 'click'
40
+ el.addEventListener(eventType, props[key]);
41
+ }
42
+ });
43
+
44
+ // 5. 자식 요소 추가
45
+ children.forEach(child => {
46
+ if (child instanceof HTMLElement) {
47
+ el.appendChild(child);
48
+ }
49
+ });
50
+
51
+ return el;
52
+ }
package/index.html ADDED
@@ -0,0 +1,19 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <body>
4
+ <div id="app"></div>
5
+ <script type="module">
6
+ import { NeoCore } from './core/NeoCore.js';
7
+ import render from './src/App.js'; // 컴파일러가 만든 결과물!
8
+
9
+ const state = { count: 0, name: "NeoVGC" };
10
+ const neo = new NeoCore(state, render, 'app');
11
+ neo.mount();
12
+
13
+ // 버튼 클릭 이벤트 (임시 연결)
14
+ document.addEventListener('click', (e) => {
15
+ if(e.target.id === 'Btn') neo.state.count++;
16
+ });
17
+ </script>
18
+ </body>
19
+ </html>
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "neo-junny-framework",
3
+ "version": "1.0.0",
4
+ "description": "나만의 커스텀 프레임워크 Neo",
5
+ "main": "core/NeoCore.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "neoc": "./compiler/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "node compiler/index.js src/App.neo",
12
+ "test": "echo \"Error: no test specified\" && exit 1"
13
+ },
14
+ "keywords": ["framework", "neo", "dsl"],
15
+ "author": "junnyontop-pixel",
16
+ "license": "MIT"
17
+ }
package/src/App.neo ADDED
@@ -0,0 +1,10 @@
1
+ @Script {
2
+ function sayHello() {
3
+ alert("안녕하세요!");
4
+ }
5
+ }
6
+
7
+ @Btn:button {
8
+ Innerhtml: "인사하기";
9
+ Event( click: sayHello() )
10
+ }