Skip to content

Commit

Permalink
build!: use parcel instead of vite
Browse files Browse the repository at this point in the history
BREAKING CHANGE: vite config files are not used anymore
  • Loading branch information
tsukinoko-kun committed Jul 27, 2024
1 parent 143cf63 commit 0fee9ea
Show file tree
Hide file tree
Showing 9 changed files with 2,170 additions and 86 deletions.
25 changes: 16 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,26 @@ pnpm add @tsukinoko-kun/ecs.ts

## Build

ECS.ts comes with a build script that is based on Vite and JSDOM.
ECS.ts comes with a build script that is based on Parcel and JSDOM.
You can of course use your own build system if you want.
Just keep in mind that ECS.ts is written in TypeScript and is not shipping any JavaScript files.

You can use a `vite.config.ts` file to configure the build process.
By default, ECS.ts expects a `src` folder with an `index.html` as entry point.

```json
```json5
{
"scripts": {
"dev": "ecs dev",
"build": "ecs build"
}
scripts: {
dev: "ecs dev",
build: "ecs build",
},
ecs: {
// source directory (default: "src")
// where to look for entry points (html files)
src: "src",
// output directory (default: "dist")
dist: "dist",
// deploy url (default: "/") can be a url (origin + base path) or a path (only base path)
// dev server will always use http://localhost:3000 + your base path
publicUrl: "https://your-domain.com/",
},
}
```

Expand Down
4 changes: 4 additions & 0 deletions apps/demo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.DS_Store
node_modules
.ECSts-cache
dist
5 changes: 5 additions & 0 deletions apps/demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,10 @@
},
"devDependencies": {
"@types/node": "^20.14.11"
},
"ecs": {
"src": "src",
"dist": "dist",
"publicUrl": "https://tsukinoko-kun.github.io/ecs.ts/"
}
}
2 changes: 1 addition & 1 deletion apps/demo/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<title>ECS Test</title>
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<link href="style.css" rel="stylesheet" />
<link href="./style.css" rel="stylesheet" />
</head>
<body>
<main id="app"></main>
Expand Down
13 changes: 13 additions & 0 deletions apps/demo/src/meep/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>ECS Test</title>
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<link href="../style.css" rel="stylesheet" />
</head>
<body>
<main id="app"></main>
<script src="../index.ts" type="module"></script>
</body>
</html>
11 changes: 0 additions & 11 deletions apps/demo/vite.config.js

This file was deleted.

188 changes: 131 additions & 57 deletions bin/ecs.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { join, relative, resolve } from "node:path"
import { join, relative } from "node:path"
import { existsSync } from "node:fs"
import { build as viteBuild, createServer as viteDev } from "vite"
import { readdir, readFile, writeFile } from "node:fs/promises"
import { JSDOM } from "jsdom"
import { Script } from "node:vm"
import { Parcel } from "@parcel/core"
import { fileURLToPath } from "node:url"

const __dirname = process.cwd()

Expand Down Expand Up @@ -31,54 +31,65 @@ async function main(args) {
}

async function build() {
const viteConfig = await getViteConfig()
const config = await getConfig()

let bundler = new Parcel({
entries: config.htmlFiles.map((file) => join(config.src, file)),
config: "@parcel/config-default",
shouldDisableCache: true,
shouldContentHash: true,
shouldAutoInstall: false,
cacheDir: join(__dirname, ".ECSts-cache"),
mode: "production",
additionalReporters: [
{
packageName: "@parcel/reporter-cli",
resolveFrom: fileURLToPath(import.meta.url),
},
],
defaultTargetOptions: {
distDir: config.dist,
publicUrl: config.parcelPublicUrl,
},
})

// build
await viteBuild(viteConfig)

// find all html files
const htmlFiles = []
const dirs = [viteConfig.build.outDir]
while (dirs.length > 0) {
const dir = dirs.pop()
const files = await readdir(dir, { withFileTypes: true })
for (const file of files) {
if (file.isDirectory()) {
dirs.push(resolve(dir, file.name))
} else if (file.isFile() && file.name.endsWith(".html")) {
htmlFiles.push(relative(viteConfig.build.outDir, resolve(dir, file.name)))
}
}
}
await bundler.run()

const adr = `http://localhost${viteConfig.base}`
const { JSDOM } = await import("jsdom")

// render html files
for (let file of htmlFiles) {
console.log("Rendering", file)
const p = resolve(viteConfig.build.outDir, file)
const dom = new JSDOM(await readFile(p, "utf-8"), {
for (let urlPath of config.htmlFiles) {
console.log("Rendering", urlPath)
const distFile = join(config.dist, urlPath)
let url = new URL(urlPath, config.publicUrl).href
if (url.endsWith("index.html")) {
url = url.slice(0, -10)
}
const dom = new JSDOM(await readFile(distFile, "utf-8"), {
runScripts: "outside-only",
resources: undefined,
url: adr + file,
url,
pretendToBeVisual: true,
})

// wait for html to be loaded
await new Promise((res) => {
dom.window.onload = res
if (dom.window.document.readyState === "complete") {
res()
}
})

// get all scripts
const scripts = Array.from(dom.window.document.querySelectorAll("script[src]"))
for (const script of scripts) {
const url = new URL(script.src, adr)
const url = new URL(script.src, config.publicUrl)
let filePath = url.pathname
if (filePath.startsWith(viteConfig.base)) {
filePath = filePath.slice(viteConfig.base.length)
if (filePath.startsWith(config.basePath)) {
filePath = filePath.slice(config.basePath.length)
}
const p = join(viteConfig.build.outDir, filePath)
const p = join(config.dist, filePath)
if (!existsSync(p)) {
continue
}
Expand All @@ -90,47 +101,110 @@ async function build() {
await delay(500)

const html = dom.serialize()
console.log("Writing", file, html)
await writeFile(resolve(viteConfig.build.outDir, file), html)
await writeFile(distFile, html)
dom.window.close()
}
}

async function dev() {
const viteConfig = await getViteConfig()
const server = await viteDev(viteConfig)
await server.listen()
const config = await getConfig()

let bundler = new Parcel({
entries: config.htmlFiles.map((file) => join(config.src, file)),
config: "@parcel/config-default",
cacheDir: join(__dirname, ".ECSts-cache"),
mode: "development",
additionalReporters: [
{
packageName: "@parcel/reporter-cli",
resolveFrom: fileURLToPath(import.meta.url),
},
],
defaultTargetOptions: {
distDir: config.dist,
publicUrl: config.basePath,
},
serveOptions: {
publicUrl: config.basePath,
port: 3000,
},
hmrOptions: {
port: 3000,
},
})

server.printUrls()
server.bindCLIShortcuts({ print: true })
}
const w = bundler.watch()

async function getViteConfig() {
const def = {
root: resolve(__dirname, "./src"),
base: "/",
build: {
outDir: resolve(__dirname, "./dist"),
emptyOutDir: true,
},
}
await delay(200)

const viteConfigPath = resolve(__dirname, "vite.config.js")
if (existsSync(viteConfigPath)) {
const config = await import(viteConfigPath)
await open("http://localhost:3000" + config.basePath)

config.default.base ||= def.base
config.default.build ??= {}
config.default.build.outDir ??= def.build.outDir
await w
}

/** @returns {Promise<{src: string, dist: string, parcelPublicUrl: string, publicUrl: string, basePath: string, htmlFiles: string[]}>} */
async function getConfig() {
// read package.json
const packageJson = JSON.parse(await readFile(join(__dirname, "package.json"), "utf-8"))

const ecsConfig = packageJson.ecs || {}
ecsConfig.src = join(__dirname, ecsConfig.src || "src")
ecsConfig.dist = join(__dirname, ecsConfig.dist || "dist")
if (ecsConfig.publicUrl) {
// public url defined in package.json
try {
const url = new URL(ecsConfig.publicUrl)
ecsConfig.publicUrl = url.href
ecsConfig.basePath = url.pathname
ecsConfig.parcelPublicUrl = url.href
} catch {
// not a valid url
const url = new URL("http://localhost")
url.pathname = ecsConfig.publicUrl
ecsConfig.publicUrl = url.href
ecsConfig.basePath = url.pathname
ecsConfig.parcelPublicUrl = url.pathname
}
}

return { ...def, ...config.default }
} else {
return def
// find all html files
const htmlFiles = []
{
const dirs = [ecsConfig.src]
while (dirs.length > 0) {
const dir = dirs.pop()
const files = await readdir(dir, { withFileTypes: true })
for (const file of files) {
if (file.isDirectory()) {
dirs.push(join(dir, file.name))
} else if (file.isFile() && file.name.endsWith(".html")) {
htmlFiles.push(relative(ecsConfig.src, join(dir, file.name)))
}
}
}
}
ecsConfig.htmlFiles = htmlFiles

return ecsConfig
}

function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
return new Promise((res) => setTimeout(res, ms))
}

async function open(url) {
const { exec } = await import("child_process")
switch (process.platform) {
case "win32":
exec(`start ${url}`)
break
case "darwin":
exec(`open ${url}`)
break
default:
exec(`xdg-open ${url}`)
break
}
}

await main(process.argv.slice(2))
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
"prettier-plugin-tailwindcss": "^0.6.5"
},
"dependencies": {
"jsdom": "^24.1.1",
"vite": "^5.3.5"
"@parcel/config-default": "^2.12.0",
"@parcel/core": "^2.12.0",
"@parcel/reporter-cli": "^2.12.0",
"jsdom": "^24.1.1"
}
}
Loading

0 comments on commit 0fee9ea

Please sign in to comment.