Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

nuekit

Package Overview
Dependencies
Maintainers
0
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nuekit - npm Package Compare versions

Comparing version 0.5.3 to 1.0.0-beta

src/browser/view-transitions.js

67

package.json
{
"name": "nuekit",
"version": "0.5.3",
"description": "The closer-to-standards web framework. Build sites and apps with less effort.",
"homepage": "https://nuejs.org",
"license": "MIT",
"type": "module",
"repository": {
"url": "https://github.com/nuejs/nue",
"directory": "packages/nuekit",
"type": "git"
},
"bin": {
"nue": "./src/cli.js"
},
"name": "nuekit",
"version": "1.0.0-beta",
"description": "Web Framework For UX Developers. Build the slickest websites in the world and wonder why you ever did them any other way",
"homepage": "https://nuejs.org",
"license": "MIT",
"type": "module",
"repository": {
"url": "https://github.com/nuejs/nue",
"directory": "packages/nuekit",
"type": "git"
},
"bin": {
"nue": "./src/cli.js"
},
"engines": {
"bun": ">= 1",
"node": ">= 18"
},
"scripts": {
"test": "cd test && bun test"
"test": "node --experimental-vm-modules ../../node_modules/jest/bin/jest.js --runInBand"
},
"dependencies": {
"diff-dom": "^5.0.6",
"es-main": "^1.3.0",
"import-meta-resolve": "^4.0.0",
"js-yaml": "^4.1.0",
"lightningcss": "^1.24.0",
"nue-glow": "latest",
"nuejs-core": "latest",
"nuemark": "latest"
},
"engines": {
"bun": ">= 1",
"node": ">= 18"
}
"dependencies": {
"diff-dom": "^5.1.4",
"es-main": "^1.3.0",
"import-meta-resolve": "^4.1.0",
"js-yaml": "^4.1.0",
"lightningcss": "^1.26.0",
"nue-glow": "*",
"nuejs-core": "*",
"nuemark": "*"
},
"devDependencies": {
"@happy-dom/global-registrator": "^14.12.3"
},
"jest": {
"setupFilesAfterEnv": [
"jest-extended/all",
"<rootDir>/../../setup-jest.js"
]
}
}
<a href="https://nuejs.org">
<img src="https://nuejs.org/img/nue-banner-big.png?1">
# Nue &nbsp;[![test](https://github.com/nuejs/nue/actions/workflows/test.yaml/badge.svg?branch=master)](https://github.com/nuejs/nue/actions/workflows/test.yaml)
<a href="https://nuejs.org/">
<img src="https://www2.nuejs.org/img/og-blue-big.png" width="900">
</a>
# Nue &nbsp;[![test](https://github.com/nuejs/nue/actions/workflows/test.yaml/badge.svg?branch=master)](https://github.com/nuejs/nue/actions/workflows/test.yaml)
Nue(kit) is a static website generator and web application builder. It's an amazingly simple and powerful alternative to **Next.js** and **Astro**:
### What is Nue?
Nue is a web framework for UX developers. What used to take a React specialist, and an absurd amount of JavaScript can now be done by a UX developer and a small amount of CSS.
[Learn how it works](https://nuejs.org/docs/)
### Getting Started
Please see https://nuejs.org/docs/
### Who is it for?
Nue is designed for the following people:
1. **UX developers**: who natively jump between **Figma** and **CSS** without confusing [designer-developer handoff](https://medium.com/design-warp/5-most-common-designer-developer-handoff-mishaps-ba96012be8a7) processes in the way.
### Why Nue?
2. **Beginner web developers**: who want to skip the [redundant frontend layers](https://roadmap.sh/frontend) and start building websites quickly with HTML, CSS, and JavaScript.
- [Content first](https://nuejs.org/docs/why-nue/content-first.html)
- [Extreme performance](https://nuejs.org/docs/why-nue/extreme-performance.html)
- [Closer to standards](https://nuejs.org/docs/why-nue/closer-to-standards.html)
3. **Experienced JS developers**: frustrated with the absurd amount of layers in the [React stack](https://roadmap.sh/react) and look for simpler ways to develop professional websites.
#### Notable features
4. **Designers**: aiming to learn web development, but find the React/JavaScript ecosystem impossible to grasp
- [Universal hot-reloading](https://nuejs.org/docs/concepts/universal-hot-reloading.html)
- [Hybrid app routing and page routing](https://nuejs.org/docs/concepts/client-side-navigation.html)
- [layout components](https://nuejs.org/docs/concepts/layout-components.html)
- [JS/TypeScript modules](https://nuejs.org/docs/concepts/js-modules.html)
- [Content collections](https://nuejs.org/docs/concepts/content-collections.html)
- [Reactive islands](https://nuejs.org/docs/concepts/reactive-islands.html)
5. **Parents & Teachers**: who wants to educate people [how the web works](https://www.websitearchitecture.co.uk/resources/examples/web-standards-model/)
### The Goal
Eventually, Nue is a content-first alternative to **Vercel** and **Netlify**, which is extremely fast and ridiculously easy to use. The goal is to be a so-called [perfect framework](https://nuejs.org/blog/perfect-web-framework/):
### Installation
<a href="https://nuejs.org/blog/perfect-web-framework/">
<img src="https://nuejs.org/img/perfect-banner-big.jpg" width="500"></a>
Check out [installation docs](https://nuejs.org/docs/installation.html)
### Roadmap
Here's the high-level roadmap. Click for a [more detailed](https://nuejs.org/blog/perfect-web-framework/#product-roadmap) version.
### Ultimate goal
Ultimately Nue will be a ridiculously simpler alternative to **Next.js**, **Gatsby**, and **Astro**
<a href="https://nuejs.org/blog/perfect-web-framework/#product-roadmap">
<img src="https://nuejs.org/img/roadmap6-big.png" width="800"></a>
[Learn more about the vision](https://nuejs.org/blog/perfect-web-framework/)
### Contributing
Please see [contributing.md](/CONTRIBUTING.md)
Please see [CONTRIBUTING.md](/CONTRIBUTING.md)

@@ -48,0 +43,0 @@

// Router for single-page applications
import { onclick, loadPage, setActive } from './page-router.js'
import { onclick, loadPage, setActive } from './view-transitions.js'

@@ -5,0 +5,0 @@ const is_browser = typeof window == 'object'

@@ -10,3 +10,3 @@

sse.onmessage = function(e) {
sse.onmessage = async function(e) {
const data = e.data ? JSON.parse(e.data) : {}

@@ -30,11 +30,15 @@ const { error, html, css, dir, url, path } = data

if (data.is_md && location.pathname != uri) location.href = uri
else patch(html)
else {
await patch(html)
dispatchEvent(new Event('reload'))
}
}
// web components cannot be re-defined :(
// web components cannot be re-mounnted :(
// if (data.is_js) import('/' + path + '?' + Math.random())
// reactive component
if (data.is_nue) remount('/' + data.path.replace('.nue', '.js'))
if (data.is_nue || data.is_htm) remount('/' + data.path.replace(data.ext, '.js'))
// styling (inline && stylesheets)

@@ -41,0 +45,0 @@ if (css) {

@@ -10,3 +10,5 @@

async function importAll(reload_path) {
// hmr_path argument only used by hotreload.js
async function importAll(hmr_path) {
const comps = document.querySelector('[name="nue:components"]')?.getAttribute('content')

@@ -18,3 +20,3 @@ if (!comps) return []

for (let path of comps.split(' ')) {
if (path == reload_path) path += `?${++remounts}`
if (path == hmr_path) path += `?${++remounts}`
const { lib } = await import(path)

@@ -27,7 +29,8 @@ if (lib) arr.push(...lib)

export async function mountAll(reload_path) {
export async function mountAll() {
const els = document.querySelectorAll('[is]')
const lib = els[0] ? await importAll(reload_path) : []
const lib = els[0] ? await importAll() : []
if (!lib[0]) return
const { createApp } = await import('./nue.js')

@@ -49,3 +52,3 @@

} else {
console.error(`Web or Nue component not fouind: "${name}"`)
// console.error(`Component not found: "${name}"`)
}

@@ -64,6 +67,10 @@ }

// mount all after route
addEventListener('route', mountAll)
addEventListener('route', () =>
// must give empty argument
mountAll()
)
// initial page load
addEventListener('DOMContentLoaded', mountAll)
addEventListener('DOMContentLoaded', () => dispatchEvent(new Event('route')))

@@ -10,4 +10,5 @@

export async function getBuilder(is_esbuild) {
const actual_cwd = process.env.ACTUAL_CWD || process.cwd()
try {
return is_esbuild ? await import(await resolve('esbuild', `file://${process.cwd()}/`)) : Bun
return is_esbuild ? await import(resolve('esbuild', `file://${actual_cwd}/`)) : Bun
} catch {

@@ -80,6 +81,5 @@ throw 'Bundler not found. Please use Bun or install esbuild'

let include = Features.Colors
if (!opts.css_2023) include |= Features.Nesting
if (opts.native_css_nesting) include |= Features.Nesting
try {
process.stdout.write('⚡️')
return transform({ code: Buffer.from(css), include, minify }).code?.toString()

@@ -90,3 +90,3 @@

title: 'CSS syntax error',
lineText: source.split('\n')[loc.line -1],
lineText: source.split(/\r\n|\r|\n/)[loc.line -1],
text: data.type,

@@ -97,3 +97,1 @@ ...loc

}

@@ -0,4 +1,3 @@

import { colors, openUrl, getVersion } from './util.js'
import { colors } from './util.js'
const HELP = `

@@ -14,2 +13,3 @@ Usage

stats Show site statistics
create Use a project starter template

@@ -20,2 +20,6 @@ Options

-e or --environment Read extra options to override defaults in site.yaml
-s or --stats Show site statistics after current command
-I or --init Force clear and initialize output directory
-n or --dry-run Show what would be built. Does not create outputs
-b or --esbuild Use esbuild as bundler. Please install it manually

@@ -40,3 +44,3 @@ File matches

# more examples
open https://nuejs.org/docs/cli
${openUrl} https://nuejs.org/docs/command-line-interface.html

@@ -46,9 +50,8 @@ Less is more

┏━┓┏┓┏┳━━┓
┃┏┓┫┃┃┃┃━┫
┃┏┓┫┃┃┃┃━┫ kit ${await getVersion()}
┃┃┃┃┗┛┃┃━┫ nuejs.org
┗┛┗┻━━┻━━┛
`
const commands = ['serve', 'build', 'stats']
const commands = ['serve', 'build', 'stats', 'create']

@@ -75,4 +78,1 @@ function formatLine(line) {

}
#!/usr/bin/env bun
import { log, colors } from './util.js'
import { log, colors, getVersion, getEngine } from './util.js'
import esMain from 'es-main'
import { sep } from 'node:path'

@@ -21,10 +22,10 @@ // [-npe] --> [-n, -p, -e]

export function getArgs(argv) {
const commands = ['serve', 'build', 'stats']
const args = { paths: [], root: '.' }
const commands = ['serve', 'build', 'stats', 'create']
const args = { paths: [], root: null }
const checkExecutable = /[\\\/]nue(\.(cmd|ps1|bunx|exe))?$/
let opt
expandArgs(argv.slice(1)).forEach((arg, i) => {
// skip
if (arg.endsWith('/cli.js') || arg.endsWith('/nue') || arg == '--') {
if (arg.endsWith(sep + 'cli.js') || checkExecutable.test(arg) || arg == '--') {

@@ -40,3 +41,3 @@ // test suite

// options
} else if (arg[0] == '-') {
} else if (!opt && arg[0] == '-') {

@@ -51,3 +52,3 @@ // booleans

else if (['-b', '--esbuild'].includes(arg)) args.esbuild = true
else if (['-P', '--push'].includes(arg)) args.push = true
else if (['-P', '--push'].includes(arg)) args.push = args.is_prod = true
else if (['-I', '--init'].includes(arg)) args.init = true

@@ -60,28 +61,15 @@

// bad options
else if (opt) throw `"${opt}" option is not set`
else throw `Unknown option: "${arg}"`
} else if (arg) {
} else if (arg && arg[0] != '-') {
if (opt) { args[opt] = arg; opt = null }
else args.paths.push(arg)
}
} else if (opt) throw `"${opt}" option is not set`
})
if (opt) throw `"${opt}" option is not set`
return args
}
// read from package.json
async function getVersion() {
const { promises } = await import('fs')
const pathname = new URL('../package.json', import.meta.url).pathname
const path = process.platform === "win32" && pathname.startsWith('/') ? pathname.slice(1) : pathname
const json = await promises.readFile(path, 'utf-8')
return JSON.parse(json).version
}
function getEngine() {
const v = process.versions
return process.isBun ? 'Bun ' + v.bun : 'Node ' + v.node
}
async function printHelp() {

@@ -100,8 +88,16 @@ const { getHelp } = await import('./cli-help.js')

const { createKit } = await import('./nuekit.js')
const { cmd='serve', dryrun, push, root=null } = args
if (!root) args.root = '.'
console.info('')
// create nue
if (cmd == 'create') {
const { create } = await import('./create.js')
return await create({ root, name: args.paths[0] })
}
const nue = await createKit(args)
args.nuekit_version = await printVersion()
const nue = await createKit(args)
const { cmd='serve', dryrun, push } = args

@@ -112,3 +108,3 @@ // stats

// build
else if (push || args.paths[0] || cmd == 'build') {
if (dryrun || push || args.paths[0] || cmd == 'build') {
const paths = await nue.build(args.paths, dryrun)

@@ -140,6 +136,2 @@

// root is required
} else if (!args.root) {
console.info('Project root not specified')
// command

@@ -146,0 +138,0 @@ } else if (!args.test) {

import { compileFile as nueCompile} from 'nuejs-core'
import { join, basename } from 'node:path'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { promises as fs } from 'node:fs'
import { resolve } from 'import-meta-resolve'
import { buildJS } from './builder.js'
import { colors } from './util.js'
import { colors, srcdir } from './util.js'

@@ -14,10 +15,6 @@

const cwd = process.cwd()
const srcdir = getSourceDir()
const fromdir = join(srcdir, 'browser')
const outdir = join(cwd, dist, '@nue')
const minify = !is_dev
// has all latest?
const latest = join(outdir, '.043')
const latest = join(outdir, '.05')
try {

@@ -34,5 +31,10 @@ if (force) doError()

// chdir hack (Bun does not support absWorkingDir)
process.chdir(srcdir)
await initDir({ dist, is_dev, esbuild, cwd, srcdir, outdir })
}
async function initDir({ dist, is_dev, esbuild, cwd, srcdir, outdir }) {
const fromdir = join(srcdir, 'browser')
const minify = !is_dev
// simple function to wtite dot

@@ -49,3 +51,3 @@ function dot() { process.stdout.write('•') }

async function copyAsset(npm_path, toname) {
const path = await resolvePath(npm_path)
const path = resolvePath(npm_path)
await fs.copyFile(path, join(outdir, toname))

@@ -65,3 +67,3 @@ dot()

bundle: true, esbuild, minify, outdir, toname,
path: await resolvePath(npm_path),
path: resolvePath(npm_path),
})

@@ -77,3 +79,3 @@ dot()

await buildPackage('nuejs-core/src/browser/nue.js', 'nue.js')
await buildFile('page-router')
await buildFile('view-transitions')
await buildFile('app-router')

@@ -83,3 +85,3 @@ await buildFile('mount')

// glow.css
await copyAsset('nue-glow/minified/glow.css', 'glow.css')
await copyAsset('nue-glow/minified/syntax.css', 'syntax.css')

@@ -97,4 +99,2 @@ // dev only

process.chdir(cwd)
// new line

@@ -105,13 +105,6 @@ console.log('')

async function resolvePath(npm_path) {
function resolvePath(npm_path) {
const [ npm_name, ...parts ] = npm_path.split('/')
let main = await resolve(npm_name, `file://${process.cwd()}/`)
main = main.replace(/^file:\/\//, '')
main = process.platform === 'win32' && main.startsWith('/') ? main.slice(1) : main
return main.replace('index.js', parts.join('/'))
const module_path = dirname(fileURLToPath(resolve(npm_name, import.meta.url)))
return join(module_path, ...parts)
}
function getSourceDir() {
const path = new URL('.', import.meta.url).pathname
return process.platform === "win32" && path.startsWith('/') ? path.slice(1) : path
}

@@ -19,4 +19,4 @@

export async function fswatch(dir, onfile, onremove) {
watch(dir, { recursive: true }, async function(e, path) {
export function fswatch(dir, onfile, onremove) {
return watch(dir, { recursive: true }, async function(e, path) {
try {

@@ -70,3 +70,3 @@ const file = parse(path)

const IGNORE = ['node_modules', 'functions', 'package.json', 'bun.lockb', 'pnpm-lock.yaml']
const IGNORE = ['node_modules', 'functions', 'package.json', 'bun.lockb', 'pnpm-lock.yaml', 'README.md']

@@ -78,3 +78,3 @@ function ignore(name='') {

function isLegit(file) {
return !ignore(file.name) && !ignore(file.dir)
return !ignore(file.name) && !ignore(file.base) && !ignore(file.dir)
}

@@ -81,0 +81,0 @@

import { log, colors, getAppDir, parsePathParts, extendData } from './util.js'
import { join, parse as parsePath, extname, basename } from 'node:path'
import { renderHead, getDefaultHTML, getDefaultSPA } from './layout.js'
import { renderPage, renderSinglePage } from './layout/page-layout.js'
import { parse as parseNue, compile as compileNue } from 'nuejs-core'
import { log, colors, getAppDir, getParts } from './util.js'
import { parsePage, renderPage } from 'nuemark/index.js'
import { lightningCSS, buildJS } from './builder.js'
import { createServer, send } from './nueserver.js'
import { lightningCSS, buildJS } from './builder.js'
import { printStats, categorize } from './stats.js'

@@ -13,2 +13,3 @@ import { promises as fs } from 'node:fs'

import { fswatch } from './nuefs.js'
import { parsePage } from 'nuemark'
import { init } from './init.js'

@@ -25,2 +26,3 @@

// site: various file based functions

@@ -37,7 +39,10 @@ const site = await createSite(args)

async function setupStyles(dir, data) {
const paths = data.styles = await site.getStyles(dir, data)
const paths = await site.getStyles(dir, data)
if (data.inline_css) {
data.inline_css = await buildAllCSS(paths)
delete data.styles
data.styles = paths.filter(path => path.includes('@nue'))
} else {
data.styles = paths
}

@@ -49,4 +54,6 @@ }

for (const path of paths) {
const { css } = await processCSS({ path, ...parsePath(path)})
arr.push({ path, css })
if (!path.startsWith('/@nue')) {
const { css } = await processCSS({ path, ...parsePath(path)})
arr.push({ path, css })
}
}

@@ -59,6 +66,6 @@ return arr

// scripts
const scripts = data.scripts = await site.getScripts(dir, data.main)
const scripts = data.scripts = await site.getScripts(dir, data)
// components
if (data.automount !== false) data.components = await site.getComponents(dir)
data.components = await site.getClientComponents(dir, data)

@@ -74,3 +81,3 @@ // system scripts

if (data.page?.isomorphic) push('nuemark')
if (data.router) push('page-router')
if (data.view_transitions || data.router) push('view-transitions')
}

@@ -81,14 +88,13 @@

const { dir } = parsePath(path)
const data = await site.getData(dir)
// markdown
// markdown data: meta, sections, headings, links
const raw = await read(path)
// { meta, sections, headings, links }
const page = parsePage(raw, data)
const page = parsePage(raw)
const { meta } = page
const { dir } = parsePath(path)
const data = await site.getData(meta.appdir || dir)
// YAML data
Object.assign(data, getParts(path), meta, { page })
Object.assign(data, parsePathParts(path), { page })
extendData(data, meta)

@@ -110,2 +116,13 @@ // content collection

// Markdown page
async function renderMPA(path) {
const data = await getPageData(path)
const file = parsePath(path)
const lib = await site.getServerComponents(data.appdir || file.dir, data)
return DOCTYPE + renderPage(data, lib)
}
// index.html for single-page application

@@ -118,3 +135,3 @@ async function renderSPA(index_path) {

const appdir = getAppDir(index_path)
const data = { ...await site.getData(appdir), ...getParts(index_path) }
const data = { ...await site.getData(appdir), ...parsePathParts(index_path) }

@@ -125,5 +142,2 @@ // scripts & styling

// head / meta tags
data.head = renderHead(data, is_prod)
// SPA components and layout

@@ -133,50 +147,11 @@ const html = await read(index_path)

if (html.includes('<html')) {
const lib = await site.getLayoutComponents(appdir)
const lib = await site.getServerComponents(appdir, data)
const [ spa, ...spa_lib ] = parseNue(html)
return DOCTYPE + spa.render(data, lib.concat(spa_lib))
return DOCTYPE + spa.render(data, [...lib, ...spa_lib])
}
const [ spa ] = parseNue(getDefaultSPA(html, data))
const [ spa ] = parseNue(renderSinglePage(html, data))
return DOCTYPE + spa.render(data)
}
// Markdown- based multi-page application page
async function renderMPA(path, data) {
const file = parsePath(path)
const dir = data.appdir || file.dir
const lib = await site.getLayoutComponents(dir)
data.content = renderPage(data.page, { data, lib }).html
function render(name, def) {
const layout = lib.find(el => el.tagName == name) || def && parseNue(def)[0]
try {
return layout ? layout.render(data, lib) : ''
} catch (e) {
delete data.inline_css
log.error(`Error in ${path}, on <${name}> component`)
throw { component: name, ...e }
}
}
data.header = render('header')
data.footer = render('footer')
data.main = render('main', '<slot for="content"/>')
data.custom_head = render('head').slice(6, -7)
data.head = renderHead(data, is_prod)
return DOCTYPE + render('html', getDefaultHTML(data))
}
// processor methods
async function processPage(file) {
const { dir, name, path } = file
const data = await getPageData(path)
const html = await renderMPA(path, data)
await write(html, dir, `${name}.html`)
return html
}
async function processScript(file) {

@@ -226,3 +201,5 @@ const { path } = file

if (file.is_md) {
const html = await processPage(file)
const { dir, name, path } = file
const html = await renderMPA(path)
await write(html, dir, `${name}.html`)
active_page = file

@@ -246,4 +223,4 @@ return { html }

// reactive component
if (file.is_nue) {
// reactive component (.nue, .htm)
if (file.is_nue || file.is_htm) {
const raw = await read(path)

@@ -266,3 +243,3 @@ const js = await compileNue(raw)

function isAssetFor(page, asset) {
if (['layout.html', 'site.yaml', 'app.yaml'].includes(asset.base)) {
if (['.html', '.yaml'].includes(asset.ext)) {
const appdir = getAppDir(asset.dir)

@@ -348,6 +325,7 @@ return ['', '.', ...site.globals].includes(appdir) || getAppDir(page.dir) == appdir

// dev mode -> watch for changes
is_dev && fswatch(root, async file => {
let watcher
if (is_dev) watcher = fswatch(root, async file => {
try {
const ret = await processFile(file)
if (ret) send({ ...file, ...getParts(file.path), ...ret })
if (ret) send({ ...file, ...parsePathParts(file.path), ...ret })
} catch (e) {

@@ -368,5 +346,14 @@ send({ error: e, ...file })

const cleanup = () => {
if (watcher) watcher.close()
}
const terminate = () => {
cleanup()
server.close()
}
try {
server.listen(port)
log(`http://localhost:${port}/`)
return terminate

@@ -386,3 +373,3 @@ } catch (e) {

// public API
build, serve, stats, dist,
build, serve, stats, dist, port,
}

@@ -389,0 +376,0 @@

import { log, getParts, getAppDir, getDirs, colors, toPosix, sortCSS } from './util.js'
import {
traverseDirsUp,
parsePathParts,
extendData,
getAppDir,
log,
colors,
toPosix,
sortCSS,
joinRootPath } from './util.js'
import { join, extname, basename, sep, parse as parsePath } from 'node:path'

@@ -36,3 +46,3 @@ import { parse as parseNue } from 'nuejs-core'

async function read(path) {
return await fs.readFile(join(root, path), 'utf-8')
return (await fs.readFile(join(root, path), 'utf-8')).replace(/\r\n|\r/g, '\n')
}

@@ -51,2 +61,3 @@

async function readOpts() {

@@ -73,7 +84,8 @@ const data = await readData('site.yaml') || {}

const {
dist = `${root}/.dist/${is_prod ? 'prod' : 'dev'}`,
dist: rawDist,
port = is_prod ? 8081 : 8080
} = site_data
const dist = joinRootPath(root, rawDist || join('.dist', is_prod ? 'prod' : 'dev'))
// flag if .dist is empty

@@ -117,3 +129,3 @@ try {

async function getPageFiles(page_dir) {
async function getPageAssets(page_dir) {
const key = ':' + page_dir

@@ -123,3 +135,3 @@ if (cache[key]) return cache[key]

for (const dir of getDirs(page_dir || '.')) {
for (const dir of traverseDirsUp(page_dir || '.')) {
try {

@@ -139,8 +151,8 @@ const paths = await fs.readdir(join(root, dir))

async function getAllFiles(from_dirs) {
const key = '@' + from_dirs
async function walkDirs(dirs) {
const key = '@' + dirs
if (cache[key]) return cache[key]
const arr = []
for (const dir of from_dirs) {
for (const dir of dirs) {
try {

@@ -161,3 +173,11 @@ const paths = await fswalk(join(root, dir))

const { include=[], exclude=[] } = data
let paths = [...await getAllFiles(self.globals), ...await getPageFiles(dir)]
const subdirs = !dir ? [] : self.globals.map(el => join(dir, el))
let paths = [
...await walkDirs(self.globals),
...await walkDirs(subdirs),
...await getPageAssets(dir)
]
const ret = []

@@ -167,4 +187,3 @@

if (include[0]) {
for (const path of await getAllFiles(self.libs)) {
for (const path of await walkDirs(self.libs)) {
// included only

@@ -205,6 +224,10 @@ if (include.find(match => toPosix(path).includes(match))) paths.push(path)

self.getData = async function (pagedir) {
const data = { nuekit_version, ...site_data }
for (const dir of getDirs(pagedir)) {
Object.assign(data, await readData(`${dir}/app.yaml`))
async function readDirData(dir) {
const paths = await getPageAssets(dir)
const data = {}
for (const path of paths) {
if (path.endsWith('.yaml')) {
Object.assign(data, await readData(path))
}
}

@@ -214,13 +237,13 @@ return data

self.walk = async function() {
return await fswalk(root)
}
self.getData = async function(pagedir) {
const data = { nuekit_version, ...site_data, is_prod }
self.getScripts = async function (dir, main=['main.js']) {
const arr = await getAssets({ dir, exts: ['js', 'ts'], to_ext: 'js' })
return arr.filter(path => main.includes(basename(path)))
for (const dir of traverseDirsUp(pagedir)) {
extendData(data, await readDirData(dir))
}
return data
}
self.getComponents = async function(dir) {
return await getAssets({ dir, exts: ['nue'], to_ext: 'js' })
self.walk = async function() {
return await fswalk(root)
}

@@ -230,31 +253,39 @@

// get fromt matter data from all .md files on a directory
self.getContentCollection = async function (dir) {
self.getContentCollection = async function(dir) {
const key = 'coll:' + dir
if (cache[key]) return cache[key]
const arr = []
// make sure dir exists
try {
await fs.stat(join(root, dir))
} catch (e) {
console.error(`content collection: "${dir}" does not exist`)
return arr
}
const paths = await fswalk(join(root, dir))
const mds = paths.filter(el => el.endsWith('.md')).map(el => join(dir, el))
const arr = []
for (const path of mds) {
const raw = await read(path)
const { meta } = nuemark(raw)
arr.push({ ...meta, ...getParts(path) })
arr.push({ ...meta, ...parsePathParts(path) })
}
arr.sort((a, b) => {
const [d1, d2] = [a, b].map(v => v.pubDate || Infinity)
const [d1, d2] = [a, b].map(v => v.pubDate || v.date || Infinity)
return d2 - d1
})
if (is_bulk) cache[key] = arr
return arr
}
self.getStyles = async function (dir, data={}) {
self.getStyles = async function(dir, data={}) {
let paths = await getAssets({ dir, exts: ['css'], data })
// syntax highlighting
if (data.page?.has_code_blocks && data.glow_css !== false) paths.push(`/@nue/glow.css`)
if (data.page?.has_code_blocks && data.syntax_highlight !== false) paths.push(`/@nue/syntax.css`)

@@ -267,8 +298,24 @@ // cascading order: globals -> area -> page

self.getScripts = async function(dir, data) {
return await getAssets({ dir, exts: ['js', 'ts'], to_ext: 'js', data })
}
self.getLayoutComponents = async function (pagedir) {
self.getClientComponents = async function(dir, data) {
return await getAssets({ dir, exts: ['nue', 'htm'], to_ext: 'js', data })
}
self.getServerComponents = async function(dir, data) {
const paths = await getAssets({ dir, exts: ['html'], data })
if (dir && dir != '.') {
const more = await fs.readdir(join(root, dir))
more.forEach(p => {
if (p.endsWith('.html')) paths.unshift(p)
})
}
const lib = []
for (const dir of ['.', ...getDirs(pagedir)]) {
const path = join(dir, `layout.html`)
for (const path of paths) {
try {

@@ -284,3 +331,2 @@ const html = await read(path)

}
return lib

@@ -290,3 +336,3 @@ }

// @returns { src, path, code: 200 }
self.getRequestPaths = async function (url) {
self.getRequestPaths = async function(url) {
let { dir, name, base, ext } = parsePath(url.slice(1))

@@ -293,0 +339,0 @@ if (!name) name = 'index'

@@ -13,2 +13,4 @@

export async function printStats(site, args) {
if (args.dryrun) return
const { dist, globals } = site

@@ -54,5 +56,5 @@ let paths = await fswalk(dist)

['js', 'ts'].includes(ext) ? cats.scripts :
ext == 'yaml' || base == 'layout.html' ? misc :
ext == 'yaml' || ext == 'html' ? misc :
base == 'index.html' ? cats.spa :
ext == 'nue' ? cats.islands :
ext == 'nue' || ext == 'htm' ? cats.islands :
ext == 'md' ? cats.pages :

@@ -59,0 +61,0 @@ cats.media

/* misc stuff. think shame.css */
import { sep, parse, normalize } from 'node:path'
import { sep, parse, normalize, join, isAbsolute, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import { promises as fs } from 'node:fs'
export const srcdir = dirname(fileURLToPath(import.meta.url))
export const openUrl = process.platform == 'darwin' ? 'open' : process.platform == 'win32' ? 'start' : 'xdg-open'
// read from package.json
export async function getVersion() {
const path = join(srcdir, '../package.json')
const json = await fs.readFile(path, 'utf-8')
return JSON.parse(json).version
}
export function getEngine() {
const v = process.versions
return process.isBun ? 'Bun ' + v.bun : 'Node ' + v.node
}
export function log(msg, extra='') {

@@ -28,13 +45,14 @@ console.log(colors.green('✓'), msg, extra)

/* path parts */
export function getParts(path) {
// returns { url, dir, slug, appdir }
export function parsePathParts(path) {
path = normalize(path)
const { dir, name, base } = parse(path)
const appdir = getAppDir(path)
const basedir = getAppDir(path)
const url = getUrl(dir, name)
return { url, dir, slug: name + '.html', appdir }
return { url, dir, slug: name + '.html', basedir }
}
export function joinRootPath(root, path, abs = false) {
return join(abs ? process.cwd() : '', isAbsolute(path) ? '' : root, path)
}

@@ -47,4 +65,4 @@ export function getAppDir(path) {

// getDirs('a/b/c') --> ['a', 'a/b', 'a/b/c']
export function getDirs(dir) {
// traverseDirsUp('a/b/c') --> ['a', 'a/b', 'a/b/c']
export function traverseDirsUp(dir) {
if (!dir) return []

@@ -59,4 +77,3 @@ dir = normalize(dir)

if (url[0] != '/') url = '/' + url
// if (name != 'index')
url += name + '.html'
if (name != 'index') url += name + '.html'
return url

@@ -69,3 +86,12 @@ }

export function extendData(to, from={}) {
const { include = [], exclude = [] } = to
if (from.include) include.push(...from.include)
if (from.exclude) exclude.push(...from.exclude)
Object.assign(to, from)
to.include = include
to.exclude = exclude
}
export function sortCSS({ paths, globals, dir }) {

@@ -87,2 +113,1 @@ function score(path) {

@@ -17,3 +17,3 @@ import { promises as fs } from 'node:fs'

test('bun init', async () => {
await init({ dist, is_dev: true })
await init({ dist, is_dev: true })
const names = await fs.readdir(join(dist, '@nue'))

@@ -24,5 +24,5 @@ expect(names.length).toBe(11)

test('esbuild init', async () => {
await init({ dist, is_dev: true, esbuild: true })
await init({ dist, is_dev: true, esbuild: true })
const names = await fs.readdir(join(dist, '@nue'))
expect(names.length).toBe(11)
})

@@ -0,7 +1,6 @@

import { match } from '../src/browser/app-router.js'
import { match } from '../src/browser/app-router.js'
import { getParts, sortCSS } from '../src/util.js'
import { parsePathParts } from '../src/util.js'
import { lightningCSS } from '../src/builder.js'
import { renderHead } from '../src/layout.js'
import { create } from '../src/create.js'
import { getArgs } from '../src/cli.js'

@@ -11,5 +10,17 @@

import { promises as fs } from 'node:fs'
expect.extend({ toMatchPath })
// temporary directory
const root = '_test'
// setup and teardown
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 }))
test('Lightning CSS errors', async () => {

@@ -36,9 +47,2 @@ try {

test('head', () => {
const head = renderHead({ charset: 'foo', title: 'Hey', preload_image: 'hey.png' })
expect(head).toInclude('meta charset="foo"')
expect(head).toInclude('<title>Hey</title>')
expect(head).toInclude('<link rel="preload" as="image" href="hey.png">')
})
test('app router', async () => {

@@ -52,8 +56,16 @@ expect(match('/fail/:id', '/users/20')).toBeNull()

test('path parts', () => {
const parts = getParts('docs/glossary/semantic-css.md')
const parts = parsePathParts('docs/glossary/semantic-css.md')
expect(parts.url).toBe('/docs/glossary/semantic-css.html')
expect(parts.dir).toMatchPath('docs/glossary')
expect(parts.appdir).toMatchPath('docs')
expect(parts.basedir).toMatchPath('docs')
expect(parts.slug).toBe('semantic-css.html')
})
test('create', async () => {
const terminate = await create({ root, name: 'test' })
terminate()
const contents = await fs.readdir(root)
expect(contents).toContain('index.md') // should be unpacked to correct dir
expect(contents).toContain('.dist') // should be built
})

@@ -52,6 +52,6 @@

test('defaults', async () => {
test('site defaults', async () => {
const site = await getSite()
expect(site.is_empty).toBe(true)
expect(site.dist).toBe('_test/.dist/dev')
expect(site.dist).toMatchPath('_test/.dist/dev')
expect(site.port).toBe(8080)

@@ -75,3 +75,3 @@ expect(site.globals).toEqual([])

expect(site.globals).toEqual(['global'])
expect(site.dist).toBe('.mydist')
expect(site.dist).toMatchPath('_test/.mydist')
expect(site.port).toBe(1500)

@@ -88,3 +88,3 @@

expect(site.dist).toBe('.alt')
expect(site.dist).toMatchPath('_test/.alt')
expect(site.port).toBe(8080)

@@ -120,6 +120,18 @@ })

test('include / exclude', async () => {
test('include/exclude data', async () => {
await write('site.yaml', 'include: [a]\nexclude: [a]')
await write('blog/app.yaml', 'include: [b]\nexclude: [b]')
await write('blog/index.md', '---\ninclude: [c]\n---\n')
const kit = await getKit()
const data = await kit.getPageData('blog/index.md')
expect(data.include).toEqual([ "a", "b", "c" ])
expect(data.exclude).toEqual([ "a", "b" ])
})
test('asset include/exclude', async () => {
await write('site.yaml', 'globals: [global]\nlibs: [lib, ext]\n')
await write('global/global.css')
await write('global/kama.css')
await write('global/kama.nue')
await write('lib/zoo.css')

@@ -133,2 +145,3 @@ await write('blog/index.md')

expect(data.styles).toEqual([ "/global/global.css", "/lib/zoo.css" ])
// expect(data.components).toEqual([ "/global/kama.js", "/lib/zoo.css" ])
})

@@ -145,3 +158,3 @@

expect(data).toEqual({ foo: 1, bar: 1, baz: 1 })
expect(data).toMatchObject({ foo: 1, bar: 1, baz: 1 })
})

@@ -196,3 +209,3 @@

// root layout
const comps = await site.getLayoutComponents()
const comps = await site.getServerComponents()
expect(comps.length).toBe(1)

@@ -202,7 +215,7 @@ expect(comps[0].tagName).toBe('header')

// app layout
const comps2 = await site.getLayoutComponents('blog')
expect(comps2.length).toBe(2)
const comps2 = await site.getServerComponents('blog')
// expect(comps2.length).toBe(2)
// page layout
const comps3 = await site.getLayoutComponents('blog/entry')
const comps3 = await site.getServerComponents('blog/entry')
expect(comps3.length).toBe(3)

@@ -213,2 +226,20 @@ expect(comps3[0].name).toBe('c')

test('page layout', async () => {
await write('site.yaml', 'header: { navi: [{ image: foo }, bar] }\nfooter: { navi: [bar] }')
await write('layout.html', '<aside>Sidebar</aside><aside @name="complementary">Aside</aside>')
await write('index.md', '# Hey')
const kit = await getKit()
const html = await kit.gen('index.md')
expect(html).toInclude('<header>')
expect(html).toInclude('<footer>')
expect(html).toInclude('<a>bar</a></nav>')
expect(html).toInclude('<aside>Sidebar</aside>')
expect(html).toInclude('<aside>Aside</aside>')
// console.info(html)
})
test('getRequestPaths', async () => {

@@ -240,3 +271,3 @@ await write('index.md')

expect(data.inline_css[0].path).toEqual('/inline/style.css')
const html = await kit.renderMPA('inline/index.md', data)
const html = await kit.gen('inline/index.md')
expect(html).toInclude('<style href="/inline/style.css">')

@@ -254,21 +285,35 @@ expect(html).toInclude('margin:')

test('line endings', async () => {
const kit = await getKit()
await write('index.md', '---\ntitle: Page title\rhero: img/image.png\r\n---\r\n\r# Hello\r\n\rWorld')
const data = await kit.getPageData('index.md')
expect(data.title).toBe('Page title')
expect(data.hero).toBe('img/image.png')
const html = await kit.gen('index.md')
expect(html).toInclude('<h1>Hello</h1>')
expect(html).toInclude('<p>World</p>')
})
test('page assets', async() => {
await write('site.yaml', 'libs: [lib]')
await write('blog/app.yaml', 'include: [video]')
await write('lib/video.nue')
await write('blog/index.md', '# Hey')
await write('blog/comp.htm', '<div/>')
await write('blog/hello.ts', 'var a')
await write('blog/main.js', 'var a')
const kit = await getKit()
await write('scripts/app.yaml', 'main: [hello.js]\nhotreload: false')
await write('scripts/index.md', '# Hey')
await write('scripts/hello.nue', '<div/>')
await write('scripts/hello.ts', 'var a')
await write('scripts/main.js', 'var a')
const data = await kit.getPageData('scripts/index.md')
const data = await kit.getPageData('blog/index.md')
expect(data.components).toEqual([ "/scripts/hello.js" ])
expect(data.scripts).toEqual([ "/scripts/hello.js", "/@nue/mount.js" ])
expect(data.components).toEqual([ "/blog/comp.js", "/lib/video.js" ])
expect(data.scripts.length).toEqual(4)
})
test('index.html', async() => {
test('single-page app index', async() => {
await write('index.html', '<test/>')
const kit = await getKit()
await kit.gen('index.html')
const html = await readDist(kit.dist, 'index.html')
const html = await kit.renderSPA('index.html')
// const html = await readDist(kit.dist, 'index.html')

@@ -280,3 +325,3 @@ expect(html).toInclude('hotreload.js')

test('index.md', async() => {
await write('index.md', '# Hey')
await write('index.md', '# Hey { .yo }\n\n## Foo { .foo#bar.baz }')
const kit = await getKit()

@@ -287,3 +332,4 @@ await kit.gen('index.md')

expect(html).toInclude('<title>Hey</title>')
expect(html).toInclude('<h1 id="hey">')
expect(html).toInclude('<h1 class="yo">Hey</h1>')
expect(html).toInclude('<h2 class="foo baz" id="bar"><a href="#bar" title="Foo"></a>Foo</h2>')
})

@@ -326,8 +372,5 @@

test.skip('random unit test', async() => {
const kit = await createKit({ root: '../nextjs-blog', dryrun: true })
})
test('the project was started for the first time', async () => {
const kit = await getKit()
await write('site.yaml', 'port: 9090')
await write('globals/bar.css')

@@ -337,6 +380,10 @@ await write('home.css')

await kit.serve()
const html = await readDist(kit.dist, 'index.html')
expect(html).toInclude('hotreload.js')
process.exit()
})
const kit = await getKit()
const terminate = await kit.serve()
try {
const html = await readDist(kit.dist, 'index.html')
expect(html).toInclude('hotreload.js')
} finally {
terminate()
}
})

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc