Table of Contents
Bugs 🐜
-
wikiLinks and 404 behaviour results in incorrect/dangling lfs due to incorrect baseUrl.
Tour of the API
return lit.fs.readdir("/testing/log");
return lit.lfs.promises.writeFile('/testing/data.json', "{}", {encoding: 'utf8'})
return lit.fs.readFile('/doesntexist.json')
return lit.fs.readStat('/manifest.json')
return lit.fs
.readStat('/manifest.json')
.then(stat => [!!stat.local.value, !!stat.remote.value])
return lit.fs
.stat('/notfound')
.catch(s => "404 Not Found")
return lit.fs
.stat('/index.lit')
.then(stat => stat)
// https://github.com/kpdecker/jsdiff
const {root,src} = lit.location
const {join} = lit.utils.path
const filename = join(root,src)
const withStats = async stats => {
const cp = lit.utils.diff.createPatch
// const f =
const local = stats.local.value
const remote = stats.remote.value
const patch = cp(filename, local, remote)
console.log(patch.split('\n').map(l=>' '+l).join('\n'))
return "logged patch"
}
const stats = lit.fs.readStat(filename, {encoding: 'utf8'})
return stats.then(withStats)
const path = "/throwaway/test.txt"
console.log(ast)
return lit.fs.writeFile(path, "content", {
encoding: 'utf8',
localOnly: true
})
Plugins
fs
plugin
return lit.utils.momento
export const viewer = ({node, React}) => {
const {useState, useEffect} = React
const {join,extname} = lit.utils.path
const [src, setSrc] = useState(node?.data?.value?.trim())
const meta = node?.properties?.meta || {}
const styles = {
dir: {fontWeight: "bold"},
'.lit': {color: 'blue'},
}
const getType = s => {
const [filepath,stat] = s
if (stat.type === 'file') {
return extname(filepath)
}
return stat.type
}
const Stat = (props) => {
const stat = props?.stat || {}
if (stat.message) return <div>{stat.message}</div>
return <div>
<div style={{marginBottom: '0.4em'}}>Type: <span>{stat.type}</span> mtime: <span>{lit.utils.momento.MsToRelative(stat.mtimeMs - Date.now())}</span> Size: <span>{(props.size / 1024).toFixed(2)} KB</span></div>
{stat.contents && stat.contents.map( l => {
const name = l[0]
const path = join(props.src,name)
const type = getType(l)
const style = styles[type] || null
return <div><span onClick={ev=> props.select(path)} style={style}>{name}</span></div>
})}
</div>
}
const [content, setContent] = useState(<span>loading...</span>)
const [stat, setStat] = useState(undefined)
const [size, setSize] = useState(null)
useEffect(async fn => {
let stat, size
try {
stat = await lit.fs.stat(src)
size = await lit.fs.du(src)
if (stat.type === 'dir') {
const list = await lit.fs.readdir(src)
const withStats = list.map( async l => [l,await lit.fs.stat(join(src,l))])
stat.contents = await Promise.all(withStats)
}
setStat(stat)
setSize(size)
} catch(err) {
setStat(err)
setSize(null)
}
}, [src])
const bigger = {fontSize: '1em', width: '100%'}
return <div style={bigger}>
<input style={bigger} value={src} onChange={ev=>setSrc(ev.target.value)}/>
<div style={{fontFamily: 'monospace', marginBottom: '0.4em'}}>
<Stat src={src} stat={stat} size={size} select={setSrc}/>
{!stat && content}
</div>
<button disabled={src === '/'} onClick={ev=> setSrc(src.split('/').slice(0,-1).join('/') || '/')}>Back</button>
{stat && <button onClick={ev=> confirm("Are you sure you want to delete local file: " + src) && lit.fs.unlink(src, true)}>Reset</button>}
{stat && <button onClick={ev=> confirm("Are you sure you want to delete local and remote file: " + src) && lit.fs.unlink(src)}>Delete</button>}
{stat && <button disabled>Diff</button>}
</div>
}
Finder (local fs)
const path = lit.utils.path;
const visit = async (root) => {
const list = await lit.fs.readdir(root);
return Promise.all(
list.map(async (key) => {
const pathname = path.join(root, key);
const stat = await lit.fs.stat(pathname);
let contents;
if (key === ".git" || !key) {
return { key, root,pathname, type: stat.type };
} else if (stat.type === "dir") {
// alert("Traversing " + pathname);
contents = await visit(pathname);
} else
contents =
(
await lit.fs.readFile(pathname, {
encoding: "utf8",
localOnly: true,
})
).slice(0, 10) + "...";
return { pathname, type: stat.type, contents };
})
);
};
return (async (fn) => {
lit.fs.writeFile(
"/testing/full.json",
JSON.stringify(await visit("/"), null, 2)
);
})();
const path = lit.utils.path;
const all = []
const visit = async (root) => {
const list = await lit.fs.readdir(root);
return Promise.all(
list.map(async (key) => {
const pathname = path.join(root, key);
all.push(pathname)
const stat = await lit.fs.stat(pathname);
if (stat.type === 'dir' && key && key !== '.git' ) await visit(pathname)
})
);
};
return (async (fn) => {
await visit('/')
return all
})();
Finder (from manifest)
const sortBy = (keys) => (a, b) => {
for (const key of keys) {
if (a[key] !== b[key]) break;
else return a[key] > b[key] ? 1 : -1;
}
};
const itemBuilder = (React) => (item) => {
const rc = React.createElement;
return rc(
"li",
{ className: "item" },
rc(
"a",
{ href: lit.href || lit.location.root + item.id },
item.title || item.id
)
);
};
export const viewer = ({ node, React }) => {
const rc = React.createElement;
const { useState, useEffect } = React;
const meta = node.properties && node.properties.meta;
const [src, setSrc] = useState(meta.search || node.data.value.trim());
const [content, setContent] = useState("Loading...");
const item = itemBuilder(React);
useEffect(async () => {
const resp = await fetch("/manifest.json");
const json = await resp.json()
let regex;
try {
regex = new RegExp(src, "i");
} catch (err) {}
const res = json.nodes
.map((x) => x)
.filter((x) => {
return (
x.id.indexOf(src) >= 0 ||
(regex && regex.test(x.id)) ||
(x.title &&
(x.title.indexOf(src) >= 0 || (regex && regex.test(x.title))))
);
})
.sort()
.map((x) => item(x));
//.join("\n")
setContent(rc("ol", null, res));
}, [src]);
return rc(
"div",
{
className: "custom-react-view",
},
[
rc("input", {
style: { width: "100%", fontSize: "1.2em" },
value: src,
onChange: (e) => setSrc(e.target.value),
}),
content,
]
);
};
Loading...
Sync local|remote|origin
// fetch all remote files and store
// locally if they don't already exist
return (async fn => {
const t = Date.now()
const p = lit.utils.path
const writePLocal = async (...args) => {
}
const m = await fetch('/manifest.json')
.then(res => res.json())
.catch(e=>({nodes:[]}))
const duds = []
const synced = []
const errors = []
const res = await Promise.all(m.nodes.map( async n => {
try {
n.stats = await lit.fs.readStat(n.id)
.then(x=>x)
.catch(err=>{
duds.push(n.id)
return {local: {}, remote: {}}
})
if (!n.stats.local.stat && n.stats.remote.stat) {
await lit.fs.writeFile(n.id, n.stats.remote.value, {localOnly: true, encoding: 'utf8'})
synced.push(n)
}
return n
} catch(err) {errors.push(n.id + " : " + err.message)}
} ))
console.log(`Synced ${synced.length}/${m.nodes.length} files in ${(Date.now() - t)/1000} seconds. Duds: ${duds.length} Errors: ${errors.length}`)
return {duds, errors}
})()
⚠️ Emergency wipe
Clicking on the following link will prompt you to confirm you want to wipe the local file system!