Comparing version 0.3.0 to 0.3.1
{ | ||
"name": "nuekit", | ||
"version": "0.3.0", | ||
"version": "0.3.1", | ||
"description": "The closer-to-standards web framework. Build sites and apps with less effort.", | ||
@@ -21,2 +21,4 @@ "homepage": "https://nuejs.org", | ||
"diff-dom": "^5.0.6", | ||
"es-main": "^1.3.0", | ||
"import-meta-resolve": "^4.0.0", | ||
"js-yaml": "^4.1.0", | ||
@@ -23,0 +25,0 @@ "nuejs-core": "^0.3.0", |
<a href="https://nuejs.org"> | ||
<img src="https://nuejs.org/img/nue-banner-big.png"> | ||
<img src="https://nuejs.org/img/nue-banner-big.png?1"> | ||
</a> | ||
@@ -5,0 +5,0 @@ |
// Router for single-page applications | ||
import { onclick, loadPage, setSelected } from './page-router.js' | ||
const is_browser = typeof window == 'object' | ||
const fns = [] | ||
@@ -15,3 +18,3 @@ | ||
// clear existing routes | ||
addEventListener('before:route', () => { | ||
is_browser && addEventListener('before:route', () => { | ||
fns.splice(0, fns.length) | ||
@@ -18,0 +21,0 @@ }) |
@@ -26,3 +26,3 @@ | ||
if (html) { | ||
if (data.is_md && location.pathname != url) location.href = url | ||
if (data.is_md && location.pathname != url) location.href = url.replace('/index.html', '/') | ||
else patch(html) | ||
@@ -29,0 +29,0 @@ } |
// MPA router | ||
// Router for multi-page applications | ||
const is_browser = typeof window == 'object' | ||
export async function loadPage(path) { | ||
dispatchEvent(new Event('before:route')) | ||
// DOM of the new page | ||
const dom = mkdom(await getHTML(path)) | ||
// title | ||
// change title | ||
document.title = $('title', dom)?.textContent | ||
// main / body | ||
const main = $('main') | ||
const main2 = $('main', dom) | ||
if (main && main2) { | ||
main.replaceWith(main2) | ||
} else { | ||
$('body').innerHTML = $('body2', dom).innerHTML | ||
$('body').classList = $('body2', dom).classList | ||
} | ||
// inline CSS | ||
@@ -29,4 +17,26 @@ const new_styles = swapStyles($$('style'), $$('style', dom)) | ||
// body class | ||
$('body').classList = $('body2', dom).classList | ||
// stylesheets | ||
// content | ||
for (const query of ['header', 'main', 'footer']) { | ||
const a = $('body >' + query) | ||
const b = $('body2 >' + query, dom) | ||
// update (if changed) | ||
if (a && b) { | ||
if (a.outerHTML != b.outerHTML) a.replaceWith(b) | ||
// remove | ||
} else if (a) { | ||
a.remove() | ||
// add | ||
} else { | ||
const fn = query == 'footer' ? 'append' : 'prepend' | ||
document.body[fn](b) | ||
} | ||
} | ||
// external CSS | ||
const paths = swapStyles($$('link'), $$('link', dom)) | ||
@@ -36,2 +46,3 @@ | ||
scrollTo(0, 0) | ||
setSelected(path) | ||
dispatchEvent(new Event('route')) | ||
@@ -42,9 +53,2 @@ }) | ||
// back button | ||
addEventListener('popstate', e => { | ||
const { path, is_spa } = e.state || {} | ||
if (path) loadPage(path) | ||
}) | ||
// setup linking | ||
@@ -70,22 +74,41 @@ export function onclick(root, fn) { | ||
// TODO: switch to aria-selected | ||
export function setSelected(path, className='selected') { | ||
const el = $(`[href="${path}"]`) | ||
// remove old selections | ||
$$('.' + className).forEach(el => el.classList.remove(className)) | ||
el?.classList.add(className) | ||
// add new ones | ||
$$(`[href="${path}"]`).forEach(el => el.classList.add(className)) | ||
} | ||
// Fix: window.onpopstate, event.state == null? | ||
// https://stackoverflow.com/questions/11092736/window-onpopstate-event-state-null | ||
is_browser && history.pushState({ path: location.pathname }, 0) | ||
// browser environment | ||
const is_browser = typeof window == 'object' | ||
if (is_browser) { | ||
// autoroute / document clicks | ||
is_browser && onclick(document, (path) => { | ||
loadPage(path) | ||
history.pushState({ path }, 0, path) | ||
setSelected(path) | ||
}) | ||
// Fix: window.onpopstate, event.state == null? | ||
// https://stackoverflow.com/questions/11092736/window-onpopstate-event-state-null | ||
history.pushState({ path: location.pathname }, 0) | ||
// autoroute / document clicks | ||
onclick(document, async path => { | ||
await loadPage(path) | ||
history.pushState({ path }, 0, path) | ||
}) | ||
// initial selected | ||
setSelected(location.pathname) | ||
// back button | ||
addEventListener('popstate', e => { | ||
const { path } = e.state || {} | ||
if (path) loadPage(path) | ||
}) | ||
} | ||
/* -------- utilities ---------- */ | ||
@@ -92,0 +115,0 @@ |
@@ -5,6 +5,7 @@ | ||
import { join, extname } from 'node:path' | ||
import { resolve } from 'import-meta-resolve' | ||
export async function getBuilder(is_esbuild) { | ||
try { | ||
return is_esbuild ? await import('esbuild') : Bun | ||
return is_esbuild ? await import(await resolve('esbuild', `file://${process.cwd()}/`)) : Bun | ||
} catch { | ||
@@ -11,0 +12,0 @@ throw 'Bundler not found. Please use Bun or install esbuild' |
#!/usr/bin/env bun | ||
import { log, colors } from './util.js' | ||
import esMain from 'es-main' | ||
@@ -47,2 +48,3 @@ // [-npe] --> [-n, -p, -e] | ||
else if (['-s', '--stats'].includes(arg)) args.stats = true | ||
else if (['-b', '--esbuild'].includes(arg)) args.esbuild = true | ||
@@ -107,2 +109,5 @@ // string values | ||
// Only run main when called as real CLI | ||
if (esMain(import.meta)) { | ||
const args = getArgs(process.argv) | ||
@@ -131,9 +136,2 @@ | ||
} |
@@ -21,3 +21,3 @@ | ||
// has all latest? | ||
const latest = join(outdir, '.020') | ||
const latest = join(outdir, '.031') | ||
try { | ||
@@ -24,0 +24,0 @@ return await fs.stat(latest) |
@@ -8,3 +8,3 @@ | ||
Auto-follows new directories and symbolic links | ||
Auto-follows new directories and symbolic (file) links | ||
@@ -25,3 +25,3 @@ Simple alternative to Chokidar and Nodemon when you "just want to watch" | ||
// skip dotfiles and files that start with "_" | ||
if (ignore(file.name) || ignore(file.dir)) return | ||
if (!isLegit(file)) return | ||
@@ -33,7 +33,18 @@ // skip double events | ||
const stat = await fs.lstat(join(dir, path)) | ||
if (!stat.isDirectory()) { | ||
await onfile({ ...file, path, size: stat.size }) | ||
last = { path, ts: Date.now() } | ||
if (stat.isDirectory()) { | ||
const paths = await fswalk(dir, path) | ||
// deploy everything on the directory | ||
for (const path of paths) { | ||
const file = parse(path) | ||
if (isLegit(file)) await onfile({ ...file, path }) | ||
} | ||
} else { | ||
if (file.ext) await onfile({ ...file, path, size: stat.size }) | ||
} | ||
last = { path, ts: Date.now() } | ||
} catch (e) { | ||
@@ -46,2 +57,5 @@ if (e.errno == -2) await onremove(path) | ||
export async function fswalk(root, _dir='', _ret=[]) { | ||
@@ -51,3 +65,3 @@ const files = await fs.readdir(join(root, _dir), { withFileTypes: true }) | ||
for (const f of files) { | ||
if (!ignore(f.name)) { | ||
if (isLegit(f)) { | ||
const path = join(_dir, f.name) | ||
@@ -64,6 +78,10 @@ if (isDir(f)) await fswalk(root, path, _ret) | ||
function ignore(name) { | ||
function ignore(name='') { | ||
return '._'.includes(name[0]) || IGNORE.includes(name) | ||
} | ||
function isLegit(file) { | ||
return !ignore(file.name) && !ignore(file.dir) | ||
} | ||
// TODO: real symdir detection | ||
@@ -70,0 +88,0 @@ function isDir(f) { |
@@ -22,3 +22,3 @@ | ||
export async function createKit(args) { | ||
const { root, is_prod, env } = args | ||
const { root, is_prod, esbuild } = args | ||
@@ -32,3 +32,3 @@ // site: various file based functions | ||
// make sure @nue dir has all the latest | ||
if (!args.dryrun) await init({ dist, is_dev }) | ||
if (!args.dryrun) await init({ dist, is_dev, esbuild }) | ||
@@ -191,2 +191,3 @@ | ||
path: join(process.cwd(), root, path), | ||
esbuild, | ||
minify: is_prod, | ||
@@ -342,4 +343,6 @@ bundle | ||
await fs.rm(dpath, { recursive: true, force: true }) | ||
send({ remove: true, path, ...parsePath(path) }) | ||
log('Removed', dpath) | ||
const file = parsePath(path) | ||
if (file.ext) send({ remove: true, path, ...file }) | ||
}) | ||
@@ -346,0 +349,0 @@ |
import { join, extname, basename, sep, parse as parsePath } from 'node:path' | ||
import { log, getParts, getAppDir, getDirs, colors } from './util.js' | ||
import { log, getParts, getAppDir, getDirs, colors, getPosixPath } from './util.js' | ||
import { parse as parseNue } from 'nuejs-core/index.js' | ||
@@ -128,3 +128,4 @@ import { nuemark } from 'nuemark/index.js' | ||
const ext = extname(path) | ||
arr.push('/' + join(dir, to_ext ? path.replace(ext, '.' + to_ext) : path)) | ||
const subpath = to_ext ? path.replace(ext, '.' + to_ext) : path | ||
arr.push('/' + getPosixPath(join(dir, subpath))) | ||
}) | ||
@@ -131,0 +132,0 @@ |
/* misc stuff. think shame.css */ | ||
import { sep, parse } from 'node:path' | ||
import { sep, parse, normalize } from 'node:path' | ||
@@ -32,2 +32,3 @@ | ||
export function getParts(path) { | ||
path = normalize(path) | ||
const { dir, name, base } = parse(path) | ||
@@ -41,2 +42,3 @@ const appdir = getAppDir(path) | ||
export function getAppDir(path) { | ||
path = normalize(path) | ||
const [ appdir ] = path.split(sep) | ||
@@ -49,2 +51,3 @@ return appdir == path ? '' : appdir | ||
if (!dir) return [] | ||
dir = normalize(dir) | ||
const els = dir.split(sep) | ||
@@ -55,3 +58,3 @@ return els.map((el, i) => els.slice(0, i + 1).join(sep)) | ||
export function getUrl(dir, name) { | ||
let url = dir.replace('\\', '/') + '/' | ||
let url = getPosixPath(dir) + '/' | ||
if (url[0] != '/') url = '/' + url | ||
@@ -62,1 +65,5 @@ // if (name != 'index') | ||
} | ||
export function getPosixPath(path) { | ||
return path.replaceAll('\\', '/') | ||
} |
@@ -9,2 +9,6 @@ | ||
import { toMatchPath } from './match-path.js' | ||
expect.extend({ toMatchPath }) | ||
const lcss = await findModule('lightningcss') | ||
@@ -89,6 +93,6 @@ const stylus = await findModule('stylus') | ||
expect(parts.url).toBe('/docs/glossary/semantic-css.html') | ||
expect(parts.dir).toBe('docs/glossary') | ||
expect(parts.appdir).toBe('docs') | ||
expect(parts.dir).toMatchPath('docs/glossary') | ||
expect(parts.appdir).toMatchPath('docs') | ||
expect(parts.slug).toBe('semantic-css.html') | ||
}) | ||
@@ -8,4 +8,7 @@ | ||
import { join, parse } from 'node:path' | ||
import { init } from '../src/init.js' | ||
import { toMatchPath } from './match-path.js' | ||
expect.extend({ toMatchPath }) | ||
// temporary directory | ||
@@ -15,3 +18,6 @@ const root = '_test' | ||
// setup and teardown | ||
beforeAll(async () => await fs.mkdir(root, { recursive: true })) | ||
beforeAll(async () => { | ||
await fs.rm(root, { recursive: true, force: true }) | ||
await fs.mkdir(root, { recursive: true }) | ||
}) | ||
afterAll(async () => await fs.rm(root, { recursive: true, force: true })) | ||
@@ -129,3 +135,3 @@ | ||
expect(coll[1].title).toBe('Second') | ||
expect(coll[1].dir).toBe('blog/nested') | ||
expect(coll[1].dir).toMatchPath('blog/nested') | ||
expect(coll[1].slug).toBe('hey.html') | ||
@@ -180,4 +186,4 @@ }) | ||
await write(path) | ||
expect(await site.getRequestPaths('/admin/')).toMatchObject({ path }) | ||
expect(await site.getRequestPaths('/admin/customers')).toMatchObject({ path }) | ||
expect((await site.getRequestPaths('/admin/')).path).toMatchPath(path) | ||
expect((await site.getRequestPaths('/admin/customers')).path).toMatchPath(path) | ||
expect(await site.getRequestPaths('/admin/readme.html')).toMatchObject({ path: '404.html' }) | ||
@@ -187,10 +193,2 @@ }) | ||
test('init dist/@nue dir', async () => { | ||
await init({ dist: root, is_dev: true, esbuild: false }) | ||
const names = await fs.readdir(join(root, '@nue')) | ||
expect(names.length).toBeGreaterThan(7) | ||
}) | ||
test('inline CSS', async () => { | ||
@@ -197,0 +195,0 @@ const kit = await getKit() |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
80160
25
1918
6
+ Addedes-main@^1.3.0
+ Addedimport-meta-resolve@^4.0.0
+ Addedes-main@1.3.0(transitive)
+ Addedimport-meta-resolve@4.1.0(transitive)