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

nuekit

Package Overview
Dependencies
Maintainers
1
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.4.3 to 0.5.0

5

package.json
{
"name": "nuekit",
"version": "0.4.3",
"version": "0.5.0",
"description": "The closer-to-standards web framework. Build sites and apps with less effort.",

@@ -24,4 +24,5 @@ "homepage": "https://nuejs.org",

"js-yaml": "^4.1.0",
"lightningcss": "^1.24.0",
"nue-glow": "latest",
"nuejs-core": "latest",
"nue-glow": "latest",
"nuemark": "latest"

@@ -28,0 +29,0 @@ },

4

src/browser/app-router.js
// Router for single-page applications
import { onclick, loadPage, setSelected } from './page-router.js'
import { onclick, loadPage, setActive } from './page-router.js'

@@ -14,3 +14,3 @@ const is_browser = typeof window == 'object'

}
setSelected(path)
setActive(path)
}

@@ -17,0 +17,0 @@

@@ -27,7 +27,11 @@

if (html) {
if (data.is_md && location.pathname != url) location.href = url.replace('/index.html', '/')
const uri = url.replace('/index.html', '/')
if (data.is_md && location.pathname != uri) location.href = uri
else patch(html)
}
// component
// web components cannot be re-defined :(
// if (data.is_js) import('/' + path + '?' + Math.random())
// reactive component
if (data.is_nue) remount('/' + data.path.replace('.nue', '.js'))

@@ -34,0 +38,0 @@

@@ -26,3 +26,3 @@

export async function mountAll(reload_path) {
const els = document.querySelectorAll('[island]')
const els = document.querySelectorAll('[is]')
const lib = els[0] ? await importAll(reload_path) : []

@@ -34,11 +34,16 @@ if (!lib[0]) return

for (const node of [...els]) {
const name = node.getAttribute('island')
const name = node.getAttribute('is')
const next = node.nextElementSibling
const data = next?.type == 'application/json' ? JSON.parse(next.textContent) : {}
const comp = lib.find(a => a.name == name)
if (comp) {
const app = createApp(comp, data, lib).mount(node)
apps.push(app)
} else if (customElements.get(name)) {
// web component -> do nothing
} else {
console.error('Component not defined:', name)
console.error(`Web or Nue component not fouind: "${name}"`)
}

@@ -45,0 +50,0 @@ }

@@ -20,3 +20,16 @@

// content
// external CSS
const paths = swapStyles($$('link'), $$('link', dom))
loadCSS(paths, () => {
updateHTML(dom)
setActive(path)
scrollTo(0, 0)
dispatchEvent(new Event('route'))
})
}
function updateHTML(dom) {
for (const query of ['header', 'main', 'footer']) {

@@ -40,14 +53,6 @@ const a = $('body >' + query)

}
}
// external CSS
const paths = swapStyles($$('link'), $$('link', dom))
loadCSS(paths, () => {
scrollTo(0, 0)
setSelected(path)
dispatchEvent(new Event('route'))
})
}
// setup linking

@@ -73,10 +78,9 @@ export function onclick(root, fn) {

// TODO: switch to aria-selected
export function setSelected(path, className='selected') {
export function setActive(path, attrname='active') {
// remove old selections
$$('.' + className).forEach(el => el.classList.remove(className))
$$(`[${attrname}]`).forEach(el => el.removeAttribute(attrname))
// add new ones
$$(`[href="${path}"]`).forEach(el => el.classList.add(className))
$$(`[href="${path}"]`).forEach(el => el.toggleAttribute(attrname, true))
}

@@ -90,2 +94,7 @@

// view transition fallback (Safari, Firefox) • caniuse.com/view-transitions
if (!document.startViewTransition) {
document.startViewTransition = (fn) => fn()
}
// Fix: window.onpopstate, event.state == null?

@@ -97,8 +106,11 @@ // https://stackoverflow.com/questions/11092736/window-onpopstate-event-state-null

onclick(document, async path => {
await loadPage(path)
history.pushState({ path }, 0, path)
document.startViewTransition(async function() {
console.info('asdlfkjasd')
await loadPage(path)
history.pushState({ path }, 0, path)
})
})
// initial selected
setSelected(location.pathname)
// initial active
setActive(location.pathname)

@@ -105,0 +117,0 @@ // back button

/* Builders for CSS, JS, and TS */
import { Features, transform } from 'lightningcss'
import { join, extname } from 'node:path'
import { resolve } from 'import-meta-resolve'
export async function getBuilder(is_esbuild) {

@@ -75,63 +77,8 @@ try {

const not_found = {}
export async function lightningCSS(css, minify) {
const include = Features.Colors | Features.Nesting
export async function findModule(name, path='') {
if (not_found[name]) return
const attempts = [
join(process.cwd(), 'node_modules', name, path),
join(process.cwd(), '..', 'node_modules', name, path),
name,
]
for (const path of attempts) {
try {
return await import(path)
} catch {}
}
not_found[name] = true
}
export async function buildCSS({ css, ext, minify }) {
// stylus
if (ext == '.styl') {
const stylus = await findModule('stylus')
if (!stylus) throw 'Module not found: "stylus"'
try {
process.stdout.write('🖌️')
return stylus.render(css, { compress: minify })
} catch({ message, stack }) {
const [l, col] = message.slice(7, message.indexOf('\n')).split(':')
throw {
title: 'Stylus syntax error',
text: stack.split('\n')[0],
lineText: css.split('\n')[l -1],
column: 1 * col,
line: 1 * l,
}
}
}
// Nue CSS (not public yet)
if (ext == '.style') {
const nuecss = await import('nuecss')
return nuecss.default(css, { minify })
}
const mod = await findModule('lightningcss', 'node/index.mjs')
// Standard CSS
if (!mod) return minify ? minifyCSS(css) : css
// Lightning CSS
process.stdout.write('⚡️')
try {
const include = mod.Features.Colors | mod.Features.Nesting
return mod.transform({ code: Buffer.from(css), include, minify }).code?.toString()
process.stdout.write('⚡️')
return transform({ code: Buffer.from(css), include, minify }).code?.toString()

@@ -149,8 +96,1 @@ } catch({ source, loc, data}) {

// temporary hack, until Bun does this natively
export function minifyCSS(code) {
return code.replace(/\s+/g, ' ')
.replace(/ ?([:{}]) /g, '$1')
// .replace(/\**?(.|\n)*?\*\//g, '')
.trim()
}

@@ -21,3 +21,3 @@

// has all latest?
const latest = join(outdir, '.034')
const latest = join(outdir, '.043')
try {

@@ -24,0 +24,0 @@ return await fs.stat(latest)

@@ -18,2 +18,3 @@

export async function fswatch(dir, onfile, onremove) {

@@ -24,3 +25,3 @@ watch(dir, { recursive: true }, async function(e, path) {

// skip dotfiles and files that start with "_"
// skip paths (files and dirs) that start with _ or .
if (!isLegit(file)) return

@@ -57,4 +58,2 @@

export async function fswalk(root, _dir='', _ret=[]) {

@@ -73,4 +72,3 @@ const files = await fs.readdir(join(root, _dir), { withFileTypes: true })

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

@@ -77,0 +75,0 @@ function ignore(name='') {

@@ -9,3 +9,3 @@

import { createServer, send } from './nueserver.js'
import { buildCSS, buildJS } from './builder.js'
import { lightningCSS, buildJS } from './builder.js'
import { promises as fs } from 'node:fs'

@@ -35,23 +35,9 @@ import { createSite } from './site.js'

async function setupStyles(dir, data) {
const paths = await site.getAssets(dir, ['style', 'css'])
const paths = data.styles = await site.getStyles(dir, data)
// alphabtical order
paths.sort()
// glow syntax
if (data.page?.has_code_blocks && data.glow_css !== false) paths.push(`/@nue/glow.css`)
if (data.inline_css) {
data.inline_css = await buildAllCSS(paths)
// prefetch global CSS
if (data.prefetch_global_css) data.prefetch = await site.getStyles()
} else {
data.styles = paths.map(path => path.replace('.style', '.css'))
delete data.styles
}
}

@@ -62,3 +48,3 @@

for (const path of paths) {
const { css } = await processStyle({ path, ...parsePath(path)})
const { css } = await processCSS({ path, ...parsePath(path)})
arr.push({ path, css })

@@ -71,8 +57,9 @@ }

// components
if (data.automount !== false) data.components = await site.getAssets(dir, ['nue'], 'js')
// scripts
const scripts = data.scripts = await site.getScripts(dir, data.include)
// components
if (data.automount !== false) data.components = await site.getComponents(dir)
// system scripts
function push(name) {

@@ -83,7 +70,6 @@ const url = `/@nue/${name}.js`

// system scripts
if (!data.is_spa && data.router) push('page-router')
if (is_dev && data.hotreload !== false) push('hotreload')
if (data.components?.length) push('mount')
if (data.page?.isomorphic) push('nuemark')
if (data.components?.length) push('mount')
if (data.router) push('page-router')
}

@@ -129,3 +115,3 @@

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

@@ -205,9 +191,12 @@ // scripts & styling

log(path)
return { bundle }
}
async function processStyle({ path, ext, name, dir}) {
async function processCSS({ path, base, dir}) {
const raw = await read(path)
const css = await buildCSS({ css: raw, ext, minify: is_prod })
await write(css, dir, `${name}.css`)
const data = await site.getData()
const css = data.lightning_css === false ? raw : await lightningCSS(raw, is_prod)
await write(css, dir, base)
return { css }

@@ -249,5 +238,4 @@ }

// style
const is_style = ['.css', '.styl', '.style'].includes(ext)
if (is_style) return await processStyle(file)
// css
if (ext == '.css') return await processCSS(file)

@@ -275,3 +263,3 @@ // reactive component

const appdir = getAppDir(asset.dir)
return ['.', ...site.globals].includes(appdir) || getAppDir(page.dir) == appdir
return ['', '.', ...site.globals].includes(appdir) || getAppDir(page.dir) == appdir
}

@@ -278,0 +266,0 @@ }

import { log, getParts, getAppDir, getDirs, colors, toPosix, sortCSS } from './util.js'
import { join, extname, basename, sep, parse as parsePath } from 'node:path'
import { log, getParts, getAppDir, getDirs, colors, getPosixPath } from './util.js'
import { parse as parseNue } from 'nuejs-core'

@@ -11,6 +11,6 @@ import { promises as fs } from 'node:fs'

// file not found error code
const NOT_FOUND = -2
// file not found error code in windows
const ENOENT = -4058
// file not found error
function fileNotFound({ errno }) {
return [-2, -4058].includes(errno)
}

@@ -20,3 +20,3 @@ export async function createSite(args) {

const { root, is_prod, env, nuekit_version } = args
const { is_bulk = args.cmd == 'build' } = args
const { build_all = args.cmd == 'build' } = args
const cache = {}

@@ -46,3 +46,3 @@

} catch (e) {
if (e.errno != NOT_FOUND && e.errno != ENOENT) {
if (!fileNotFound(e)) {
throw `YAML parse error in ${path}`

@@ -67,4 +67,8 @@ } else if (path == env) throw e

let site_data = await readOpts()
const self = { globals: site_data.globals || [] }
const self = {
globals: site_data.globals || [],
libs: site_data.libs || [],
}
const {

@@ -89,7 +93,7 @@ dist = `${root}/.dist/${is_prod ? 'prod' : 'dev'}`,

await fs.writeFile(to, content)
!is_bulk && !self.is_empty && log(join(dir, filename))
!build_all && !self.is_empty && log(join(dir, filename))
return to
} catch (e) {
if (e.errno != NOT_FOUND && e.errno != ENOENT) throw e
if (!fileNotFound(e)) throw e
await fs.mkdir(todir, { recursive: true })

@@ -106,6 +110,6 @@ return await write(content, dir, filename)

await fs.copyFile(join(root, dir, base), to)
!is_bulk && !self.is_empty && log(join(dir, base))
!build_all && !self.is_empty && log(join(dir, base))
} catch (e) {
if (e.errno != NOT_FOUND && e.errno != ENOENT) throw e
if (!fileNotFound(e)) throw e
await fs.mkdir(join(dist, dir), { recursive: true })

@@ -116,36 +120,73 @@ await copy(file)

async function getAssets(dir, exts, to_ext) {
const dirs = [...self.globals, ...getDirs(dir || '.')]
const key = ':' + dir + exts
async function getPageFiles(page_dir) {
const key = ':' + page_dir
if (cache[key]) return cache[key]
const arr = []
if (cache[key]) return cache[key]
for (const dir of dirs) {
for (const dir of getDirs(page_dir || '.')) {
try {
const paths = self.globals.includes(dir) ? await fswalk(join(root, dir)) : await fs.readdir(join(root, dir))
const paths = await fs.readdir(join(root, dir))
paths.forEach(path => arr.push(join(dir, path)))
paths.filter(path => {
const ext = extname(path).slice(1)
return exts.includes(ext)
} catch (e) {
if (!fileNotFound(e)) return console.error(e)
}
}
}).forEach(path => {
const ext = extname(path)
const subpath = to_ext ? path.replace(ext, '.' + to_ext) : path
arr.push('/' + getPosixPath(join(dir, subpath)))
})
if (build_all) cache[key] = arr
return arr
}
async function getAllFiles(from_dirs) {
const key = '@' + from_dirs
if (cache[key]) return cache[key]
const arr = []
for (const dir of from_dirs) {
try {
const paths = await fswalk(join(root, dir))
paths.forEach(path => arr.push(join(dir, path)))
} catch (e) {
if (e.errno != NOT_FOUND && e.errno != ENOENT) return console.error(e)
if (!fileNotFound(e)) return console.error(e)
}
}
if (is_bulk) cache[key] = arr
if (build_all) cache[key] = arr
return arr
}
async function getAssets({ dir, exts, to_ext, data={} }) {
const { include=[], exclude=[] } = data
let paths = [...await getAllFiles(self.globals), ...await getPageFiles(dir)]
const ret = []
// library files
if (include[0]) {
for (const path of await getAllFiles(self.libs)) {
// included only
if (include.find(match => toPosix(path).includes(match))) paths.push(path)
}
}
paths.forEach(path => {
const ext = extname(path)
path = '/' + toPosix(path)
// include / exclude
if (exts.includes(ext.slice(1)) && !exclude.find(match => path.includes(match))) {
if (to_ext) path = path.replace(ext, '.' + to_ext)
ret.push(path)
}
})
return ret
}
self.update = async function() {
site_data = await readOpts()
const { globals=[] } = site_data
const { globals=[], libs=[] } = site_data

@@ -156,2 +197,7 @@ if ('' + self.globals != '' + globals) {

}
if ('' + self.libs != '' + libs) {
self.libs = libs
return true
}
}

@@ -172,6 +218,11 @@

self.getScripts = async function (dir, include=['main.js']) {
const arr = await getAssets(dir, ['js', 'ts'], 'js')
const arr = await getAssets({ dir, exts: ['js', 'ts'], to_ext: 'js' })
return arr.filter(path => include.includes(basename(path)))
}
self.getComponents = async function(dir) {
return await getAssets({ dir, exts: ['nue'], to_ext: 'js' })
}
// get fromt matter data from all .md files on a directory

@@ -196,3 +247,3 @@ self.getContentCollection = async function (dir) {

})
if (is_bulk) cache[key] = arr
if (build_all) cache[key] = arr
return arr

@@ -202,6 +253,16 @@ }

self.getStyles = async function (dir) {
return await getAssets(dir, ['style', 'css'], 'css')
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`)
// cascading order: globals -> area -> page
sortCSS({ paths, globals: self.globals, dir })
return paths
}
self.getLayoutComponents = async function (pagedir) {

@@ -216,3 +277,3 @@ const lib = []

} catch (e) {
if (e.errno != NOT_FOUND && e.errno != ENOENT) {
if (!fileNotFound(e)) {
log.error('parse error', path)

@@ -254,4 +315,4 @@ console.error(e)

return { ...self, dist, port, read, write, copy, getAssets }
return { ...self, dist, port, read, write, copy }
}

@@ -87,3 +87,3 @@

const cat = ['style', 'css', 'styl'].includes(ext) ? cats.style :
const cat = ext == 'css' ? cats.style :
['js', 'ts'].includes(ext) ? cats.scripts :

@@ -90,0 +90,0 @@ ext == 'yaml' || base == 'layout.html' ? misc :

@@ -55,3 +55,3 @@

export function getUrl(dir, name) {
let url = getPosixPath(dir) + '/'
let url = toPosix(dir) + '/'
if (url[0] != '/') url = '/' + url

@@ -63,4 +63,23 @@ // if (name != 'index')

export function getPosixPath(path) {
export function toPosix(path) {
return path.replaceAll('\\', '/')
}
export function sortCSS({ paths, globals, dir }) {
function score(path) {
if (path[0] == '/') path = path.slice(1)
const appdir = getAppDir(path)
const els = path.split(sep)
return globals.includes(appdir) ? 0 : dir == appdir ? (els[2] ? 3 : 2) : 1
}
// alphabetical first
paths.sort()
// then by directory
paths.sort((a, b) => score(a)-score(b))
}

@@ -1,2 +0,2 @@

import { getPosixPath } from '../src/util.js'
import { toPosix } from '../src/util.js'

@@ -8,3 +8,3 @@ // https://stackoverflow.com/questions/67325342/how-to-run-os-agnostic-jest-test-files-that-check-paths

const pass = getPosixPath(actual) == expected
const pass = toPosix(actual) == expected

@@ -11,0 +11,0 @@ return {

import { buildCSS, findModule } from '../src/builder.js'
import { getParts } from '../src/util.js'
import { match } from '../src/browser/app-router.js'
import { getParts, sortCSS } from '../src/util.js'
import { lightningCSS } from '../src/builder.js'
import { renderHead } from '../src/layout.js'

@@ -13,10 +13,6 @@ import { getArgs } from '../src/cli.js'

const lcss = await findModule('lightningcss')
const stylus = await findModule('stylus')
test('Lightning CSS errors', async () => {
if (!lcss) return
try {
await buildCSS({ css: 'body margin: 0 }', minify: true })
await lightningCSS('body margin: 0 }', true)
} catch (e) {

@@ -29,42 +25,6 @@ expect(e.lineText).toBe('body margin: 0 }')

test('Lightning CSS', async () => {
if (lcss) {
const css = await buildCSS({ css: 'body { margin: 0 }', minify: true })
expect(css).toBe('body{margin:0}')
}
})
test('Stylus error', async () => {
if (!stylus) return
try {
const css = await buildCSS({ css: 'foo { mb: 0', ext: '.styl' })
} catch (e) {
expect(e.line).toBe(1)
expect(e.column).toBe(12)
expect(e.lineText).toBe('foo { mb: 0')
}
})
test('Stylus', async () => {
if (!stylus) return
const css = await buildCSS({ css: 'body\n margin: 0', ext: '.styl', minify: true })
const css = await lightningCSS('body { margin: 0 }', true)
expect(css).toBe('body{margin:0}')
})
test('Lightning CSS errors', async () => {
if (!lcss) return
try {
await buildCSS({ css: 'body margin: 0 }', minify: true })
} catch (e) {
expect(e.lineText).toBe('body margin: 0 }')
expect(e.line).toBe(1)
}
})
test('Lightning CSS', async () => {
if (!lcss) return
const css = await buildCSS({ css: 'body { margin: 0 }', minify: true })
expect(css).toBe('body{margin:0}')
})
test('CLI args', () => {

@@ -71,0 +31,0 @@ const args = getArgs(['nue', 'build', '--verbose', '-pnve', 'joku.yaml'])

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

function createFront(title, pubDate) {
return ['---', `title: ${title}`, `pubDate: ${pubDate ?? '2020-01-20'}`, '---'].join('\n')
return ['---', `title: ${title}`, `pubDate: ${pubDate ?? '2020-01-20'}`, '---'].join('\n')
}

@@ -91,4 +91,22 @@

test('page styles', async () => {
await write('site.yaml', 'globals: [globals]')
const site = await getSite()
await write('globals/foo.css')
await write('blog/b.css')
await write('blog/entry/c.css')
const arr = await site.getStyles('blog')
expect(arr).toEqual([ '/globals/foo.css', '/blog/b.css' ])
const arr2 = await site.getStyles('blog/entry')
expect(arr2.length).toBe(3)
})
test('root styles', async () => {
const kit = await getKit()
await write('globals/bar.css')
await write('home.css')

@@ -98,14 +116,17 @@ await write('index.md')

expect(styles).toEqual([ "/home.css" ])
})
test('page assets', async () => {
const site = await getSite()
await write('blog/b.js')
await write('blog/entry/c.js')
expect(await site.getAssets('blog', ['js'])).toEqual(['/blog/b.js'])
test('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('lib/zoo.css')
await write('blog/index.md')
await write('blog/app.yaml', 'include: [lib]\nexclude: [kama]')
expect(await site.getAssets('blog/entry', ['js']))
.toEqual(['/blog/b.js', '/blog/entry/c.js'])
const kit = await getKit()
const data = await kit.getPageData('blog/index.md')
expect(data.styles).toEqual([ "/global/global.css", "/lib/zoo.css" ])
})

@@ -226,3 +247,3 @@

test('page scripts', async() => {
test('page assets', async() => {
const kit = await getKit()

@@ -248,3 +269,3 @@ await write('scripts/app.yaml', 'include: [hello.js]\nhotreload: false')

expect(html).toInclude('hotreload.js')
expect(html).toInclude('island="test"')
expect(html).toInclude('is="test"')
})

@@ -251,0 +272,0 @@

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