eclipsa 0.1.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/core/component.ts +7 -0
- package/core/dev-client/mod.ts +17 -0
- package/core/dev-client/renderer.ts +5 -0
- package/core/dev-client/types.ts +3 -0
- package/core/mod.ts +4 -0
- package/core/signal.ts +20 -0
- package/core/types.ts +6 -0
- package/deno.json +23 -0
- package/jsx/jsx-dev-runtime.ts +19 -0
- package/jsx/jsx-runtime.ts +1 -0
- package/jsx/mod.ts +34 -0
- package/jsx/shared.ts +1 -0
- package/jsx/types.ts +23 -0
- package/mod.ts +2 -0
- package/package.json +4 -0
- package/transformers/dev-client/mod.ts +39 -0
- package/transformers/dev-ssr/mod.ts +52 -0
- package/transformers/utils/jsx.ts +77 -0
- package/utils/node-connect.ts +55 -0
- package/vite/dev-app/mod.ts +88 -0
- package/vite/mod.ts +76 -0
- package/vite/utils/routing.ts +26 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { hydrate } from './renderer.ts'
|
|
2
|
+
import type { DevClientInfo } from './types.ts'
|
|
3
|
+
|
|
4
|
+
const getDevInfo = (): DevClientInfo => {
|
|
5
|
+
const elem = document.getElementById('eclipsa-devinfo')
|
|
6
|
+
|
|
7
|
+
if (!elem) {
|
|
8
|
+
throw new Error('devinfo element is falsy.')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return JSON.parse(elem.innerHTML)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const initDevClient = async () => {
|
|
15
|
+
const Component = (await import(/* @vite-ignore */getDevInfo().filePath)).default
|
|
16
|
+
hydrate(Component, document.body)
|
|
17
|
+
}
|
package/core/mod.ts
ADDED
package/core/signal.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { signal } from 'alien-signals'
|
|
2
|
+
|
|
3
|
+
interface Signal<T> {
|
|
4
|
+
value: T
|
|
5
|
+
}
|
|
6
|
+
interface UseSignal {
|
|
7
|
+
<T>(v: T): Signal<T>
|
|
8
|
+
<T>(v?: T | undefined): Signal<T | undefined>
|
|
9
|
+
}
|
|
10
|
+
export const useSignal: UseSignal = (value) => {
|
|
11
|
+
const sig = signal(value)
|
|
12
|
+
return {
|
|
13
|
+
get value() {
|
|
14
|
+
return sig.get()
|
|
15
|
+
},
|
|
16
|
+
set value(value) {
|
|
17
|
+
sig.set(value)
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
}
|
package/core/types.ts
ADDED
package/deno.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xely/eclipsa",
|
|
3
|
+
"exports": {
|
|
4
|
+
".": "./mod.ts",
|
|
5
|
+
"./vite": "./vite/mod.ts",
|
|
6
|
+
"./jsx-runtime": "./jsx/jsx-runtime.ts",
|
|
7
|
+
"./jsx-dev-runtime": "./jsx/jsx-dev-runtime.ts",
|
|
8
|
+
"./jsx": "./jsx/mod.ts"
|
|
9
|
+
},
|
|
10
|
+
"imports": {
|
|
11
|
+
"@babel/core": "npm:@babel/core@^7.25.8",
|
|
12
|
+
"@babel/plugin-syntax-jsx": "npm:@babel/plugin-syntax-jsx@^7.25.7",
|
|
13
|
+
"@babel/plugin-transform-react-jsx": "npm:@babel/plugin-transform-react-jsx@^7.25.7",
|
|
14
|
+
"@babel/traverse": "npm:@babel/traverse@^7.25.7",
|
|
15
|
+
"@babel/types": "npm:@babel/types@^7.25.8",
|
|
16
|
+
"@types/babel__core": "npm:@types/babel__core@^7.20.5",
|
|
17
|
+
"@types/babel__traverse": "npm:@types/babel__traverse@^7.20.6",
|
|
18
|
+
"alien-signals": "npm:alien-signals@^0.0.6",
|
|
19
|
+
"babel-plugin-jsx-dom-expressions": "npm:babel-plugin-jsx-dom-expressions@^0.39.2",
|
|
20
|
+
"hono": "npm:hono@^4.6.4",
|
|
21
|
+
"vite": "npm:vite@^6.0.0-beta.2"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FRAGMENT } from './shared.ts'
|
|
2
|
+
import type { JSX } from './types.ts'
|
|
3
|
+
|
|
4
|
+
interface Source {
|
|
5
|
+
fileName: string
|
|
6
|
+
}
|
|
7
|
+
export const jsxDEV = (
|
|
8
|
+
type: JSX.Type,
|
|
9
|
+
props: Record<string, unknown>,
|
|
10
|
+
key: string | number | symbol,
|
|
11
|
+
isStatic: boolean,
|
|
12
|
+
_source: Source,
|
|
13
|
+
): JSX.Element => ({
|
|
14
|
+
type,
|
|
15
|
+
props,
|
|
16
|
+
key,
|
|
17
|
+
isStatic,
|
|
18
|
+
})
|
|
19
|
+
export const Fragment = FRAGMENT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { JSX } from './types.ts'
|
package/jsx/mod.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { JSX } from './jsx-runtime.ts'
|
|
2
|
+
import { FRAGMENT } from './shared.ts'
|
|
3
|
+
|
|
4
|
+
export const renderToString = (elem: JSX.Element): string => {
|
|
5
|
+
if (!elem) {
|
|
6
|
+
return ''
|
|
7
|
+
}
|
|
8
|
+
if (typeof elem === 'string' || typeof elem === 'boolean' || typeof elem === 'number') {
|
|
9
|
+
return elem.toString()
|
|
10
|
+
}
|
|
11
|
+
if (typeof elem.type === 'function') {
|
|
12
|
+
return renderToString(elem.type(elem.props))
|
|
13
|
+
}
|
|
14
|
+
let attrText = ''
|
|
15
|
+
for (const [k, v] of Object.entries(elem.props)) {
|
|
16
|
+
switch (k) {
|
|
17
|
+
case 'children':
|
|
18
|
+
break
|
|
19
|
+
default: {
|
|
20
|
+
attrText += `${k}="${v}"`
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
let childrenText = ''
|
|
25
|
+
if (Array.isArray(elem.props.children)) {
|
|
26
|
+
for (const child of elem.props.children) {
|
|
27
|
+
childrenText += renderToString(child)
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
childrenText += renderToString(elem.props.children as JSX.Element)
|
|
31
|
+
}
|
|
32
|
+
const result = elem.type === FRAGMENT ? childrenText : `<${elem.type} ${attrText}>${childrenText}</${elem.type}>`
|
|
33
|
+
return result
|
|
34
|
+
}
|
package/jsx/shared.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const FRAGMENT = '__ECLIPSA_FRAGMENT'
|
package/jsx/types.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// deno-lint-ignore no-namespace
|
|
2
|
+
export namespace JSX {
|
|
3
|
+
export type Type = string | ((props: unknown) => Element)
|
|
4
|
+
export type Childable = Element
|
|
5
|
+
export type Element = {
|
|
6
|
+
type: Type
|
|
7
|
+
props: Record<string, unknown>
|
|
8
|
+
key?: string | number | symbol
|
|
9
|
+
isStatic: boolean
|
|
10
|
+
} | string | number | undefined | null | boolean
|
|
11
|
+
|
|
12
|
+
export interface IntrinsicAttributes {
|
|
13
|
+
key?: any
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface IntrinsicElements {
|
|
17
|
+
[name: string]: any
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ElementChildrenAttribute {
|
|
21
|
+
children?: any
|
|
22
|
+
}
|
|
23
|
+
}
|
package/mod.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// @ts-types="@types/babel__core"
|
|
2
|
+
import { transform} from '@babel/core'
|
|
3
|
+
// @ts-types="@types/babel__traverse"
|
|
4
|
+
import type { Visitor } from '@babel/traverse'
|
|
5
|
+
import SyntaxJSX from '@babel/plugin-syntax-jsx'
|
|
6
|
+
import { getJSXType, transformChildren, transformProps } from '../utils/jsx.ts'
|
|
7
|
+
import * as t from '@babel/types'
|
|
8
|
+
|
|
9
|
+
const pluginClientDevJSX = () => {
|
|
10
|
+
return {
|
|
11
|
+
inherits: SyntaxJSX.default,
|
|
12
|
+
visitor: {
|
|
13
|
+
JSXElement(path) {
|
|
14
|
+
const openingElement = path.node.openingElement
|
|
15
|
+
const type = getJSXType(openingElement)
|
|
16
|
+
const { props } = transformProps(openingElement)
|
|
17
|
+
const children = transformChildren(path.node)
|
|
18
|
+
props.properties.push(t.objectProperty(t.stringLiteral('children'), children))
|
|
19
|
+
|
|
20
|
+
const fn = t.arrowFunctionExpression([], t.objectExpression([
|
|
21
|
+
t.objectProperty(t.stringLiteral('type'), type),
|
|
22
|
+
t.objectProperty(t.stringLiteral('props'), props),
|
|
23
|
+
]))
|
|
24
|
+
path.replaceWith(fn)
|
|
25
|
+
}
|
|
26
|
+
} satisfies Visitor
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const transformClientDevJSX = (input: string) => {
|
|
31
|
+
const resultCode = transform(input, {
|
|
32
|
+
plugins: [pluginClientDevJSX()],
|
|
33
|
+
sourceMaps: 'inline'
|
|
34
|
+
})?.code
|
|
35
|
+
if (!resultCode) {
|
|
36
|
+
throw new Error('Compiling JSX was failed.')
|
|
37
|
+
}
|
|
38
|
+
return resultCode
|
|
39
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// @ts-types="@types/babel__core"
|
|
2
|
+
import { transform, types as t } from '@babel/core'
|
|
3
|
+
// @ts-types="@types/babel__traverse"
|
|
4
|
+
import type { Visitor } from '@babel/traverse'
|
|
5
|
+
import SyntaxJSX from '@babel/plugin-syntax-jsx'
|
|
6
|
+
import { getJSXType, transformChildren, transformProps } from '../utils/jsx.ts'
|
|
7
|
+
|
|
8
|
+
const pluginJSX = () => {
|
|
9
|
+
return {
|
|
10
|
+
inherits: SyntaxJSX.default,
|
|
11
|
+
visitor: {
|
|
12
|
+
Program: {
|
|
13
|
+
enter(path) {
|
|
14
|
+
const jsxDEV = t.identifier('jsxDEV')
|
|
15
|
+
const importDeclaration = t.importDeclaration([
|
|
16
|
+
t.importSpecifier(jsxDEV, jsxDEV)
|
|
17
|
+
], t.stringLiteral('@xely/eclipsa/jsx-dev-runtime'))
|
|
18
|
+
|
|
19
|
+
path.unshiftContainer('body', importDeclaration)
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
JSXElement(path) {
|
|
23
|
+
const openingElement = path.node.openingElement
|
|
24
|
+
|
|
25
|
+
const type = getJSXType(openingElement)
|
|
26
|
+
const { props, key } = transformProps(openingElement)
|
|
27
|
+
const children = transformChildren(path.node)
|
|
28
|
+
props.properties.push(t.objectProperty(t.stringLiteral('children'), children))
|
|
29
|
+
|
|
30
|
+
const fn = t.callExpression(t.identifier('jsxDEV'), [
|
|
31
|
+
type,
|
|
32
|
+
props,
|
|
33
|
+
key ?? t.nullLiteral(),
|
|
34
|
+
t.booleanLiteral(false)
|
|
35
|
+
])
|
|
36
|
+
path.replaceWith(fn)
|
|
37
|
+
},
|
|
38
|
+
} satisfies Visitor,
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const transformJSX = (code: string): string => {
|
|
43
|
+
const resultCode = transform(code, {
|
|
44
|
+
plugins: [pluginJSX()],
|
|
45
|
+
sourceMaps: 'inline'
|
|
46
|
+
})?.code
|
|
47
|
+
|
|
48
|
+
if (!resultCode) {
|
|
49
|
+
throw new Error('Compiling JSX was failed.')
|
|
50
|
+
}
|
|
51
|
+
return resultCode
|
|
52
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// @ts-types="@types/babel__core"
|
|
2
|
+
import { types as t } from '@babel/core'
|
|
3
|
+
|
|
4
|
+
export const transformProps = (elem: t.JSXOpeningElement) => {
|
|
5
|
+
const propArr: (t.ObjectProperty | t.SpreadElement)[] = []
|
|
6
|
+
let key: t.Expression | undefined
|
|
7
|
+
for (const attr of elem.attributes) {
|
|
8
|
+
if (t.isJSXSpreadAttribute(attr)) {
|
|
9
|
+
propArr.push(t.spreadElement(attr.argument))
|
|
10
|
+
continue
|
|
11
|
+
}
|
|
12
|
+
if (t.isJSXNamespacedName(attr.name)) {
|
|
13
|
+
throw new Error('JSXNamespacedName is not supported.')
|
|
14
|
+
}
|
|
15
|
+
const isKey = attr.name.name === 'key'
|
|
16
|
+
const name = t.stringLiteral(attr.name.name)
|
|
17
|
+
if (attr.value === null) {
|
|
18
|
+
propArr.push(t.objectProperty(name, t.booleanLiteral(true)))
|
|
19
|
+
break
|
|
20
|
+
}
|
|
21
|
+
if (t.isStringLiteral(attr.value)) {
|
|
22
|
+
if (isKey) {
|
|
23
|
+
key = attr.value
|
|
24
|
+
}
|
|
25
|
+
propArr.push(t.objectProperty(name, attr.value))
|
|
26
|
+
continue
|
|
27
|
+
}
|
|
28
|
+
if (t.isJSXExpressionContainer(attr.value)) {
|
|
29
|
+
if (t.isJSXEmptyExpression(attr.value.expression)) {
|
|
30
|
+
continue
|
|
31
|
+
}
|
|
32
|
+
if (isKey) {
|
|
33
|
+
key = attr.value.expression
|
|
34
|
+
}
|
|
35
|
+
propArr.push(t.objectProperty(name, attr.value.expression))
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
props: t.objectExpression(propArr),
|
|
41
|
+
key,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const UPPER_CASE_REGEX = /[A-Z]/
|
|
46
|
+
export const getJSXType = (elem: t.JSXOpeningElement) => {
|
|
47
|
+
if (elem.name.type !== 'JSXIdentifier') {
|
|
48
|
+
throw new TypeError('expected JSXIdentifier')
|
|
49
|
+
}
|
|
50
|
+
const name = elem.name.name
|
|
51
|
+
if (UPPER_CASE_REGEX.test(name[0])) {
|
|
52
|
+
return t.identifier(name)
|
|
53
|
+
}
|
|
54
|
+
return t.stringLiteral(name)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const transformChildren = (elem: t.JSXElement) => t.arrayExpression(
|
|
58
|
+
elem.children.map((child) => {
|
|
59
|
+
if (t.isJSXText(child)) {
|
|
60
|
+
const str = child.value.trim()
|
|
61
|
+
if (str === '') {
|
|
62
|
+
return null
|
|
63
|
+
}
|
|
64
|
+
return t.stringLiteral(str)
|
|
65
|
+
}
|
|
66
|
+
if (t.isJSXExpressionContainer(child)) {
|
|
67
|
+
if (t.isJSXEmptyExpression(child.expression)) {
|
|
68
|
+
return null
|
|
69
|
+
}
|
|
70
|
+
return child.expression
|
|
71
|
+
}
|
|
72
|
+
if (t.isJSXElement(child)) {
|
|
73
|
+
return child as unknown as t.Expression
|
|
74
|
+
}
|
|
75
|
+
return null
|
|
76
|
+
}).filter(Boolean)
|
|
77
|
+
)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IncomingMessage,
|
|
3
|
+
ServerResponse,
|
|
4
|
+
} from 'node:http'
|
|
5
|
+
|
|
6
|
+
export const incomingMessageToRequest = (
|
|
7
|
+
incomingMessage: IncomingMessage,
|
|
8
|
+
): Request => {
|
|
9
|
+
const body =
|
|
10
|
+
(incomingMessage.method !== 'GET' && incomingMessage.method !== 'HEAD')
|
|
11
|
+
? new ReadableStream<Uint8Array>({
|
|
12
|
+
start(controller) {
|
|
13
|
+
incomingMessage.on('data', (chunk) => {
|
|
14
|
+
controller.enqueue(new Uint8Array(chunk))
|
|
15
|
+
})
|
|
16
|
+
incomingMessage.on('end', () => {
|
|
17
|
+
controller.close()
|
|
18
|
+
})
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
: null
|
|
22
|
+
const headers = new Headers()
|
|
23
|
+
for (const [k, v] of Object.entries(incomingMessage.headers)) {
|
|
24
|
+
if (Array.isArray(v)) {
|
|
25
|
+
for (const value of v) {
|
|
26
|
+
headers.append(k, value)
|
|
27
|
+
}
|
|
28
|
+
} else if (v) {
|
|
29
|
+
headers.append(k, v)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return new Request(new URL(incomingMessage.url ?? '', 'http://localhost'), {
|
|
33
|
+
method: incomingMessage.method,
|
|
34
|
+
body,
|
|
35
|
+
headers,
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const responseForServerResponse = async (
|
|
40
|
+
res: Response,
|
|
41
|
+
serverRes: ServerResponse,
|
|
42
|
+
) => {
|
|
43
|
+
for (const [k, v] of res.headers) {
|
|
44
|
+
serverRes.setHeader(k, v)
|
|
45
|
+
}
|
|
46
|
+
serverRes.statusCode = res.status
|
|
47
|
+
serverRes.statusMessage = res.statusText
|
|
48
|
+
|
|
49
|
+
if (res.body) {
|
|
50
|
+
for await (const chunk of res.body) {
|
|
51
|
+
serverRes.write(chunk)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
serverRes.end()
|
|
55
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Hono, type Context } from 'hono'
|
|
2
|
+
import type { DevEnvironment, ResolvedConfig, ViteDevServer } from 'vite'
|
|
3
|
+
import type { ModuleRunner } from 'vite/module-runner'
|
|
4
|
+
import { renderToString } from '../../jsx/mod.ts'
|
|
5
|
+
import type { SSRRootProps } from '../../core/types.ts'
|
|
6
|
+
import { Fragment } from '../../jsx/jsx-dev-runtime.ts'
|
|
7
|
+
import { createRoutes, type RouteEntry } from '../utils/routing.ts'
|
|
8
|
+
import type { DevClientInfo } from '../../core/dev-client/types.ts'
|
|
9
|
+
|
|
10
|
+
interface DevAppInit {
|
|
11
|
+
resolvedConfig: ResolvedConfig
|
|
12
|
+
devServer: ViteDevServer
|
|
13
|
+
runner: ModuleRunner
|
|
14
|
+
ssrEnv: DevEnvironment
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const createDevApp = async (init: DevAppInit) => {
|
|
18
|
+
const app = new Hono()
|
|
19
|
+
|
|
20
|
+
const createHandler = (entry: RouteEntry) => async (c: Context) => {
|
|
21
|
+
const [
|
|
22
|
+
{ default: Page },
|
|
23
|
+
{ default: SSRRoot }
|
|
24
|
+
] = await Promise.all([
|
|
25
|
+
await init.runner.import(entry.filePath),
|
|
26
|
+
await init.runner.import('/app/+ssr-root.tsx')
|
|
27
|
+
])
|
|
28
|
+
|
|
29
|
+
const page = Page()
|
|
30
|
+
const parent = SSRRoot({
|
|
31
|
+
children: page,
|
|
32
|
+
head: {
|
|
33
|
+
type: Fragment,
|
|
34
|
+
isStatic: true,
|
|
35
|
+
props: {
|
|
36
|
+
children: [
|
|
37
|
+
{
|
|
38
|
+
type: 'script',
|
|
39
|
+
isStatic: true,
|
|
40
|
+
props: {
|
|
41
|
+
children: 'import("/@vite/client")'
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
type: 'script',
|
|
46
|
+
props: {
|
|
47
|
+
type: 'module',
|
|
48
|
+
src: '/app/+client.dev.tsx'
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
type: 'script',
|
|
53
|
+
isStatic: true,
|
|
54
|
+
props: {
|
|
55
|
+
type: 'text/eclipsa+devinfo',
|
|
56
|
+
id: 'eclipsa-devinfo',
|
|
57
|
+
children: JSON.stringify({
|
|
58
|
+
filePath: entry.filePath
|
|
59
|
+
} satisfies DevClientInfo)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} satisfies SSRRootProps)
|
|
66
|
+
|
|
67
|
+
return c.html(renderToString(parent))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (const entry of await createRoutes(init.resolvedConfig.root)) {
|
|
71
|
+
app.get(entry.honoPath, createHandler(entry))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return app
|
|
75
|
+
}
|
|
76
|
+
export const createDevFetch = (
|
|
77
|
+
init: DevAppInit,
|
|
78
|
+
): (req: Request) => Promise<Response | undefined> => {
|
|
79
|
+
let app = createDevApp(init)
|
|
80
|
+
|
|
81
|
+
return async (req) => {
|
|
82
|
+
const fetched = await (await app).fetch(req)
|
|
83
|
+
if (fetched.status === 404) {
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
return fetched
|
|
87
|
+
}
|
|
88
|
+
}
|
package/vite/mod.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createServerModuleRunner,
|
|
3
|
+
DevEnvironment,
|
|
4
|
+
type Plugin,
|
|
5
|
+
type ResolvedConfig,
|
|
6
|
+
} from 'vite'
|
|
7
|
+
import { createDevFetch } from './dev-app/mod.ts'
|
|
8
|
+
import {
|
|
9
|
+
incomingMessageToRequest,
|
|
10
|
+
responseForServerResponse,
|
|
11
|
+
} from '../utils/node-connect.ts'
|
|
12
|
+
import { transformJSX } from '../transformers/dev-ssr/mod.ts'
|
|
13
|
+
import { transformClientDevJSX } from '../transformers/dev-client/mod.ts'
|
|
14
|
+
|
|
15
|
+
export const eclipsa = (): Plugin => {
|
|
16
|
+
let config: ResolvedConfig
|
|
17
|
+
return {
|
|
18
|
+
name: 'vite-plugin-eclipsa',
|
|
19
|
+
config() {
|
|
20
|
+
return {
|
|
21
|
+
esbuild: {
|
|
22
|
+
jsxFactory: 'jsx',
|
|
23
|
+
jsxImportSource: '@xely/eclipsa',
|
|
24
|
+
jsx: 'preserve',
|
|
25
|
+
},
|
|
26
|
+
//environments: {
|
|
27
|
+
/* ssr: {
|
|
28
|
+
dev: {
|
|
29
|
+
createEnvironment(name, config, _context) {
|
|
30
|
+
return new DevEnvironment(name, config, {
|
|
31
|
+
hot: false,
|
|
32
|
+
})
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},*/
|
|
36
|
+
// },
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
configResolved(resolvedConfig) {
|
|
40
|
+
config = resolvedConfig
|
|
41
|
+
},
|
|
42
|
+
configureServer(server) {
|
|
43
|
+
const ssrEnv = server.environments.ssr
|
|
44
|
+
const runner = createServerModuleRunner(ssrEnv, {
|
|
45
|
+
hmr: false,
|
|
46
|
+
})
|
|
47
|
+
const devFetch = createDevFetch({
|
|
48
|
+
resolvedConfig: config,
|
|
49
|
+
devServer: server,
|
|
50
|
+
runner,
|
|
51
|
+
ssrEnv,
|
|
52
|
+
})
|
|
53
|
+
server.middlewares.use(async (req, res, next) => {
|
|
54
|
+
const webReq = incomingMessageToRequest(req)
|
|
55
|
+
const webRes = await devFetch(webReq)
|
|
56
|
+
if (webRes) {
|
|
57
|
+
responseForServerResponse(webRes, res)
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
next()
|
|
61
|
+
})
|
|
62
|
+
},
|
|
63
|
+
hotUpdate(options) {
|
|
64
|
+
options.server.hot.send({ type: 'full-reload' })
|
|
65
|
+
},
|
|
66
|
+
transform(code, id) {
|
|
67
|
+
if (id.endsWith('.tsx')) {
|
|
68
|
+
const result = (this.environment.name === 'ssr' ? transformJSX : transformClientDevJSX)(code)
|
|
69
|
+
return {
|
|
70
|
+
code: result
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import fg from 'fast-glob'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
// WIP
|
|
5
|
+
const filePathToHonoPath = (filePath: string) => {
|
|
6
|
+
const segments = filePath.split('/').slice(0, -1)
|
|
7
|
+
|
|
8
|
+
return segments.join('/') || '/'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface RouteEntry {
|
|
12
|
+
filePath: string
|
|
13
|
+
honoPath: string
|
|
14
|
+
}
|
|
15
|
+
export const createRoutes = async (root: string): Promise<RouteEntry[]> => {
|
|
16
|
+
const appDir = path.join(root, 'app')
|
|
17
|
+
const result = []
|
|
18
|
+
for await (const entry of fg.stream(path.join(root, '/**/+page.tsx'))) {
|
|
19
|
+
const relativePath = path.relative(appDir, entry.toString())
|
|
20
|
+
result.push({
|
|
21
|
+
filePath: entry.toString(),
|
|
22
|
+
honoPath: filePathToHonoPath(relativePath)
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
return result
|
|
26
|
+
}
|