katabatic 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,46 @@
1
+
2
+
3
+ # Katabatic
4
+
5
+ A simple and efficient way to develop Web Components !
6
+
7
+ Katabatic is a compiler that transforms HTML modules into JavaScript.
8
+ Its goal is to provide a modern, framework-free web development experience by relying solely on standard APIs.
9
+
10
+ - :sparkles: No API ! Write plain HTML, CSS and JS without having to learn yet an other framework.
11
+ - :heart: Web Components without the complexity
12
+ - :zap: Reactive with signals
13
+
14
+ ```html
15
+ <script type="module">
16
+ export class Counter extends HTMLElement {
17
+ count = 0
18
+ }
19
+
20
+ customElements.define('my-counter', Counter);
21
+ </script>
22
+
23
+ <template>
24
+ <div>
25
+ <button onclick="{count++}">counter is {count}</button>
26
+ </div>
27
+ <style>
28
+ button {
29
+ padding: 0.5rem;
30
+ text-transform: uppercase;
31
+ }
32
+ </style>
33
+ </template>
34
+ ```
35
+
36
+ ## Getting started
37
+
38
+ ```
39
+ npm i katabatic -D
40
+ npm i @katabatic/runtime
41
+
42
+ npx katabatic
43
+
44
+ ```
45
+
46
+ See the [examples](examples)
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "katabatic",
3
+ "license": "MIT",
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/katabatic-js/katabatic.git",
9
+ "directory": "packages/katabatic"
10
+ },
11
+ "keywords": [
12
+ "katabatic",
13
+ "web-components",
14
+ "signals"
15
+ ],
16
+ "bin": {
17
+ "katabatic": "src/index.js"
18
+ },
19
+ "dependencies": {
20
+ "@katabatic/compiler": "^1.0.0",
21
+ "@web/dev-server": "^0.4.6"
22
+ }
23
+ }
package/src/build.js ADDED
@@ -0,0 +1,37 @@
1
+ import path from 'path'
2
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'
3
+ import { compile } from '@katabatic/compiler'
4
+ import { createRouter, compileHtmlIndex } from '@katabatic/compiler/router'
5
+
6
+ export function buildModule({ src, out, ...context }) {
7
+ const source = readFileSync(src.filePath, { encoding: 'utf8' })
8
+ const { code, ...module } = compile(source, context)
9
+
10
+ if (!existsSync(out.parentPath)) {
11
+ mkdirSync(out.parentPath, { recursive: true })
12
+ }
13
+ writeFileSync(out.filePath, code)
14
+
15
+ return module
16
+ }
17
+
18
+ export function buildIndex({ src, out, ...context }) {
19
+ const source = readFileSync(src.filePath, { encoding: 'utf8' })
20
+ const { code, ...index } = compileHtmlIndex(source, context)
21
+
22
+ if (!existsSync(out.parentPath)) {
23
+ mkdirSync(out.parentPath, { recursive: true })
24
+ }
25
+ writeFileSync(out.filePath, code)
26
+
27
+ return index
28
+ }
29
+
30
+ export function buildRouter(outDirPath, registry) {
31
+ const result = createRouter(registry)
32
+
33
+ if (!existsSync(outDirPath)) {
34
+ mkdirSync(outDirPath, { recursive: true })
35
+ }
36
+ writeFileSync(path.join(outDirPath, 'router.js'), result.code)
37
+ }
package/src/index.js ADDED
@@ -0,0 +1,155 @@
1
+ #! /usr/bin/env node
2
+
3
+ import path from 'path'
4
+ import { watch as watchDir, existsSync } from 'fs'
5
+ import { startDevServer } from '@web/dev-server'
6
+ import { walkDir } from './utils/files.js'
7
+ import * as argv from './utils/argv.js'
8
+ import { serve } from './serve.js'
9
+ import { buildIndex, buildModule, buildRouter } from './build.js'
10
+ import { hash } from './utils/misc.js'
11
+
12
+ const command = argv.command()
13
+
14
+ const rootPath = path.resolve(argv.option('root')) ?? process.cwd()
15
+ const srcDirPath = path.join(rootPath, argv.option('srcDir') ?? '.')
16
+ const outDirPath = path.join(rootPath, argv.option('outDir') ?? '.')
17
+ const routesDirPath = path.join(srcDirPath, argv.option('routesDir') ?? './routes')
18
+ const hasRoutes = existsSync(routesDirPath)
19
+
20
+ const rewriteRelativeImportExtensions = argv.option('rewriteRelativeImportExtensions') ?? false
21
+
22
+ switch (command) {
23
+ case 'build':
24
+ build()
25
+ break
26
+ default:
27
+ watch(build())
28
+ start()
29
+ break
30
+ }
31
+
32
+ function build() {
33
+ const registry = {}
34
+
35
+ walkDir(srcDirPath, {}, (entry) => {
36
+ if (entry.name === 'index.html') {
37
+ if (!hasRoutes) return
38
+
39
+ const srcFilePath = path.join(entry.parentPath, entry.name)
40
+ const index = buildIndex(resolve(srcFilePath))
41
+ registry[index.ref] = index
42
+ return
43
+ }
44
+
45
+ if (entry.name.endsWith('.html') || entry.name.endsWith('.ktb')) {
46
+ const srcFilePath = path.join(entry.parentPath, entry.name)
47
+ const module = buildModule(resolve(srcFilePath, true))
48
+ registry[module.ref] = module
49
+ return
50
+ }
51
+ })
52
+
53
+ if (hasRoutes) {
54
+ buildRouter(outDirPath, registry)
55
+ }
56
+ return registry
57
+ }
58
+
59
+ function watch(registry) {
60
+ watchDir(srcDirPath, { recursive: true }, (event, fileName) => {
61
+ if (fileName.endsWith('index.html')) {
62
+ if (!hasRoutes) return
63
+
64
+ const srcFilePath = path.join(srcDirPath, fileName)
65
+ const index = buildIndex(resolve(srcFilePath))
66
+ registry[index.ref] = index
67
+ return
68
+ }
69
+
70
+ if (fileName.endsWith('.html') || fileName.endsWith('.ktb')) {
71
+ const srcFilePath = path.join(srcDirPath, fileName)
72
+ const module = buildModule(resolve(srcFilePath, true))
73
+ registry[module.ref] = module
74
+ return
75
+ }
76
+ })
77
+ }
78
+
79
+ function start() {
80
+ const plugins = []
81
+
82
+ if (hasRoutes) {
83
+ plugins.push(serve(outDirPath))
84
+ }
85
+
86
+ startDevServer({
87
+ config: {
88
+ rootDir: rootPath,
89
+ port: 3000,
90
+ watch: true,
91
+ open: true,
92
+ nodeResolve: true,
93
+ plugins
94
+ },
95
+ readCliArgs: false,
96
+ readFileConfig: false
97
+ })
98
+ }
99
+
100
+ function resolve(srcFilePath, isModule) {
101
+ const srcFileName = path.basename(srcFilePath)
102
+ const srcParentPath = path.dirname(srcFilePath)
103
+
104
+ const parentPath = path.join(outDirPath, path.relative(srcDirPath, srcParentPath))
105
+ const fileName = srcFileName.replace(/\.html|\.ktb$/, '.js')
106
+ const filePath = path.join(parentPath, fileName)
107
+
108
+ const ref = path.relative(srcDirPath, srcFilePath)
109
+ let routerImport = `./${path.relative(outDirPath, filePath)}`
110
+
111
+ if (isModule) {
112
+ const name = path.basename(fileName, '.js')
113
+ const moduleHash = hash(srcFilePath)
114
+
115
+ let route = path.relative(routesDirPath, srcParentPath)
116
+ route = route.startsWith('..') ? undefined : `/${route}`
117
+ routerImport = !!route ? routerImport : undefined
118
+ const isPage = !!route && !!srcFileName.match(/^page(\.html|\.ktb)$/)
119
+ const isLayout = !!route && !!srcFileName.match(/^layout(\.html|\.ktb)$/)
120
+
121
+ return {
122
+ ref,
123
+ name,
124
+ customElementName: `${name}-${moduleHash}`,
125
+ route,
126
+ isPage,
127
+ isLayout,
128
+ routerImport,
129
+ hash: moduleHash,
130
+ rewriteRelativeImportExtensions,
131
+ src: {
132
+ filePath: srcFilePath
133
+ },
134
+ out: {
135
+ parentPath,
136
+ fileName,
137
+ filePath
138
+ }
139
+ }
140
+ }
141
+
142
+ return {
143
+ ref,
144
+ isIndex: true,
145
+ routerImport,
146
+ src: {
147
+ filePath: srcFilePath
148
+ },
149
+ out: {
150
+ parentPath,
151
+ fileName,
152
+ filePath
153
+ }
154
+ }
155
+ }
package/src/serve.js ADDED
@@ -0,0 +1,14 @@
1
+ import path from 'path'
2
+
3
+ export function serve(outDirPath) {
4
+ let router
5
+
6
+ return {
7
+ name: 'serve',
8
+ async serve({ request }) {
9
+ router ??= await import(path.join(outDirPath, 'router.js'))
10
+ console.log(request.url)
11
+ return router.route(request.url)
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,12 @@
1
+ export function command() {
2
+ return process.argv[2]
3
+ }
4
+
5
+ export function option(name) {
6
+ const index = process.argv.indexOf(`-${name}`)
7
+
8
+ if (index >= 0) {
9
+ const value = process.argv[index + 1]
10
+ return value && value[0] !== '-' ? value : undefined
11
+ }
12
+ }
@@ -0,0 +1,14 @@
1
+ import { readdirSync } from 'fs'
2
+ import path from 'path'
3
+
4
+ export function walkDir(dirPath, state, handle) {
5
+ const entries = readdirSync(dirPath, { withFileTypes: true })
6
+
7
+ for (const entry of entries) {
8
+ if (entry.isDirectory() && entry.name !== 'node_modules') {
9
+ walkDir(path.join(entry.parentPath, entry.name), state, handle)
10
+ } else if (entry.isFile()) {
11
+ handle(entry, state)
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,7 @@
1
+ export function hash(str) {
2
+ let hash = 5381
3
+ let i = str.length
4
+
5
+ while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i)
6
+ return (hash >>> 0).toString(36)
7
+ }