@abreen/tada 1.5.7 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/build/build-manifest.ts +2 -1
- package/build/bundle.ts +7 -5
- package/build/copy.ts +4 -9
- package/build/generate-content-assets.ts +2 -4
- package/build/utils/content-files.ts +3 -9
- package/build/utils/paths.ts +16 -1
- package/build/utils/render.ts +16 -12
- package/build/utils/trace.ts +2 -5
- package/build/validate-internal-links-plugin.ts +12 -1
- package/build/watch-reload-client.ts +1 -0
- package/build/watch.ts +7 -24
- package/package.json +8 -7
- package/src/style.scss +2 -2
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Tada :tada:
|
|
2
2
|
|
|
3
|
+
[](https://github.com/abreen/Tada/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@abreen/tada)
|
|
5
|
+
|
|
3
6
|
A static site generator. The successor to Presto.
|
|
4
7
|
|
|
5
8
|
## Features
|
package/build/build-manifest.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { toPosix } from './utils/paths';
|
|
3
4
|
|
|
4
5
|
const EXCLUDED_DIRS = new Set(['pagefind']);
|
|
5
6
|
const EXCLUDED_FILES = new Set(['tada.manifest.json']);
|
|
@@ -68,7 +69,7 @@ export async function walkAndHash(
|
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
const abs = path.join(entry.parentPath, entry.name);
|
|
71
|
-
const rel = path.relative(dir, abs)
|
|
72
|
+
const rel = toPosix(path.relative(dir, abs));
|
|
72
73
|
|
|
73
74
|
const topLevel = rel.split('/')[0];
|
|
74
75
|
if (EXCLUDED_DIRS.has(topLevel)) {
|
package/build/bundle.ts
CHANGED
|
@@ -3,7 +3,12 @@ import os from 'os';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import _ from 'lodash';
|
|
5
5
|
import * as sass from 'sass';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
getPackageDir,
|
|
8
|
+
getProjectDir,
|
|
9
|
+
getDistDir,
|
|
10
|
+
toPosix,
|
|
11
|
+
} from './utils/paths';
|
|
7
12
|
import { deriveTheme } from './utils/derive-theme';
|
|
8
13
|
import type { PluginBuilder } from 'bun';
|
|
9
14
|
import type { SiteVariables } from './types';
|
|
@@ -104,10 +109,7 @@ export async function bundle(
|
|
|
104
109
|
|
|
105
110
|
// Return the output filenames for asset tag injection
|
|
106
111
|
const assetFiles = result.outputs.map(output =>
|
|
107
|
-
path
|
|
108
|
-
.relative(resolvedDistDir, output.path)
|
|
109
|
-
.split(path.sep)
|
|
110
|
-
.join(path.posix.sep),
|
|
112
|
+
toPosix(path.relative(resolvedDistDir, output.path)),
|
|
111
113
|
);
|
|
112
114
|
|
|
113
115
|
return assetFiles;
|
package/build/copy.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { toPosix } from './utils/paths';
|
|
3
4
|
import { makeLogger } from './log';
|
|
4
5
|
import { B } from './colors';
|
|
5
6
|
|
|
@@ -22,7 +23,7 @@ function collectFiles(dir: string): CollectedFile[] {
|
|
|
22
23
|
.map(entry => {
|
|
23
24
|
const abs = path.join(entry.parentPath, entry.name);
|
|
24
25
|
const rel = path.relative(dir, abs);
|
|
25
|
-
return { abs, rel: rel
|
|
26
|
+
return { abs, rel: toPosix(rel) };
|
|
26
27
|
});
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -83,10 +84,7 @@ export function copyPublicFile(
|
|
|
83
84
|
filePath: string,
|
|
84
85
|
contentAssetRelPaths?: Set<string>,
|
|
85
86
|
): void {
|
|
86
|
-
const rel = path
|
|
87
|
-
.relative(publicDir, filePath)
|
|
88
|
-
.split(path.sep)
|
|
89
|
-
.join(path.posix.sep);
|
|
87
|
+
const rel = toPosix(path.relative(publicDir, filePath));
|
|
90
88
|
if (contentAssetRelPaths && contentAssetRelPaths.has(rel)) {
|
|
91
89
|
log.error`public/${B`${rel}`} conflicts with content/${B`${rel}`}`;
|
|
92
90
|
throw new Error(`public/${rel} and content/${rel} have the same path`);
|
|
@@ -103,10 +101,7 @@ export function copyContentFile(
|
|
|
103
101
|
filePath: string,
|
|
104
102
|
publicRelPaths?: Set<string>,
|
|
105
103
|
): void {
|
|
106
|
-
const rel = path
|
|
107
|
-
.relative(contentDir, filePath)
|
|
108
|
-
.split(path.sep)
|
|
109
|
-
.join(path.posix.sep);
|
|
104
|
+
const rel = toPosix(path.relative(contentDir, filePath));
|
|
110
105
|
if (publicRelPaths && publicRelPaths.has(rel)) {
|
|
111
106
|
log.error`content/${B`${rel}`} conflicts with public/${B`${rel}`}`;
|
|
112
107
|
throw new Error(`content/${rel} and public/${rel} have the same path`);
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
} from './util';
|
|
17
17
|
import { isLiterateJava } from './utils/file-types';
|
|
18
18
|
import { checkJavac } from './utils/literate-java';
|
|
19
|
+
import { toPosix } from './utils/paths';
|
|
19
20
|
import type {
|
|
20
21
|
SiteVariables,
|
|
21
22
|
Asset,
|
|
@@ -291,10 +292,7 @@ export class ContentRenderer {
|
|
|
291
292
|
for (const filePath of buildContentFiles) {
|
|
292
293
|
if (isLiterateJava(filePath)) {
|
|
293
294
|
const parsed = path.parse(path.relative(contentDir, filePath));
|
|
294
|
-
const javaPath = path
|
|
295
|
-
.join(parsed.dir, parsed.name)
|
|
296
|
-
.split(path.sep)
|
|
297
|
-
.join(path.posix.sep);
|
|
295
|
+
const javaPath = toPosix(path.join(parsed.dir, parsed.name));
|
|
298
296
|
literateJavaOutputPaths.add(`/${javaPath}`);
|
|
299
297
|
}
|
|
300
298
|
}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
isLiterateJava,
|
|
8
8
|
isPartial,
|
|
9
9
|
} from './file-types';
|
|
10
|
-
import { getPublicDir, normalizeOutputPath } from './paths';
|
|
10
|
+
import { getPublicDir, normalizeOutputPath, toPosix } from './paths';
|
|
11
11
|
|
|
12
12
|
function walkFiles(dir: string): string[] {
|
|
13
13
|
return fs.readdirSync(dir).flatMap(file => {
|
|
@@ -111,18 +111,12 @@ export function getValidInternalTargets(
|
|
|
111
111
|
const relPath = path.relative(contentDir, filePath);
|
|
112
112
|
const parsed = path.parse(relPath);
|
|
113
113
|
const ext = parsed.ext.toLowerCase();
|
|
114
|
-
const subPath = path
|
|
115
|
-
.join(parsed.dir, parsed.name)
|
|
116
|
-
.split(path.sep)
|
|
117
|
-
.join(path.posix.sep);
|
|
114
|
+
const subPath = toPosix(path.join(parsed.dir, parsed.name));
|
|
118
115
|
|
|
119
116
|
if (isLiterateJava(filePath)) {
|
|
120
117
|
// parsed.name is e.g. "VowelCounter.java"; the output page is
|
|
121
118
|
// VowelCounter.java.html and the source file is VowelCounter.java
|
|
122
|
-
const javaSubPath = path
|
|
123
|
-
.join(parsed.dir, parsed.name)
|
|
124
|
-
.split(path.sep)
|
|
125
|
-
.join(path.posix.sep);
|
|
119
|
+
const javaSubPath = toPosix(path.join(parsed.dir, parsed.name));
|
|
126
120
|
addGeneratedRouteAliases(targets, `/${javaSubPath}.html`);
|
|
127
121
|
targets.add(normalizeOutputPath(`/${javaSubPath}`));
|
|
128
122
|
} else if (extensionIsMarkdown(ext) || ext === '.html') {
|
package/build/utils/paths.ts
CHANGED
|
@@ -45,8 +45,23 @@ export function createApplyBasePath(
|
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
export function toPosix(p: string): string {
|
|
49
|
+
return p.split(path.sep).join(path.posix.sep);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Convert an OS-native relative filesystem path to a URL path: convert
|
|
54
|
+
* separators to '/', then percent-encode each segment so characters that
|
|
55
|
+
* are unsafe in URLs (space, ?, #, non-ASCII, etc.) are encoded. Use this
|
|
56
|
+
* anywhere a filesystem-derived path is emitted as an href or looked up
|
|
57
|
+
* against a URL-encoded href.
|
|
58
|
+
*/
|
|
59
|
+
export function toUrlPath(p: string): string {
|
|
60
|
+
return toPosix(p).split('/').map(encodeURIComponent).join('/');
|
|
61
|
+
}
|
|
62
|
+
|
|
48
63
|
export function normalizeOutputPath(outputPath: string): string {
|
|
49
|
-
const normalized = path.posix.normalize(outputPath);
|
|
64
|
+
const normalized = path.posix.normalize(toPosix(outputPath));
|
|
50
65
|
if (normalized === '.' || normalized === '') {
|
|
51
66
|
return '/';
|
|
52
67
|
}
|
package/build/utils/render.ts
CHANGED
|
@@ -17,7 +17,12 @@ import {
|
|
|
17
17
|
import { extensionIsMarkdown } from './file-types';
|
|
18
18
|
import { createTraceHelpers } from './trace';
|
|
19
19
|
import { createIncludeFunction } from './include';
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
createApplyBasePath,
|
|
22
|
+
normalizeOutputPath,
|
|
23
|
+
toPosix,
|
|
24
|
+
toUrlPath,
|
|
25
|
+
} from './paths';
|
|
21
26
|
import { parseFrontMatterAndContent } from './front-matter';
|
|
22
27
|
import { createMarkdown } from './markdown';
|
|
23
28
|
import { generateTocHtml, generateCodeTocHtml } from '../toc-plugin';
|
|
@@ -164,10 +169,7 @@ export function injectKatexStylesheet(
|
|
|
164
169
|
}
|
|
165
170
|
|
|
166
171
|
function toContentAssetPath(contentDir: string, filePath: string): string {
|
|
167
|
-
return path
|
|
168
|
-
.relative(contentDir, filePath)
|
|
169
|
-
.split(path.sep)
|
|
170
|
-
.join(path.posix.sep);
|
|
172
|
+
return toPosix(path.relative(contentDir, filePath));
|
|
171
173
|
}
|
|
172
174
|
|
|
173
175
|
export function renderPlainTextPageAsset({
|
|
@@ -182,7 +184,7 @@ export function renderPlainTextPageAsset({
|
|
|
182
184
|
javacAvailable,
|
|
183
185
|
}: RenderPlainTextOptions): Asset[] {
|
|
184
186
|
const { dir, name, ext } = path.parse(filePath);
|
|
185
|
-
const subPath = path.relative(contentDir, path.join(dir, name));
|
|
187
|
+
const subPath = toPosix(path.relative(contentDir, path.join(dir, name)));
|
|
186
188
|
const applyBasePath = createApplyBasePath(siteVariables);
|
|
187
189
|
|
|
188
190
|
log.info`Rendering page ${B`${subPath + ext}`}`;
|
|
@@ -248,11 +250,11 @@ export function renderCodePageAsset({
|
|
|
248
250
|
assetFiles,
|
|
249
251
|
}: RenderCodePageOptions): Asset[] {
|
|
250
252
|
const { dir, name, ext } = path.parse(filePath);
|
|
251
|
-
const subPath = path.relative(contentDir, path.join(dir, name));
|
|
253
|
+
const subPath = toPosix(path.relative(contentDir, path.join(dir, name)));
|
|
252
254
|
const applyBasePath = createApplyBasePath(siteVariables);
|
|
253
255
|
const lang = siteVariables.codeLanguages![ext.slice(1).toLowerCase()];
|
|
254
256
|
const sourceCode = fs.readFileSync(filePath, 'utf-8');
|
|
255
|
-
const pageDirPath = path.relative(contentDir, dir);
|
|
257
|
+
const pageDirPath = toPosix(path.relative(contentDir, dir));
|
|
256
258
|
|
|
257
259
|
log.info`Rendering code page ${B`${subPath + ext}`}`;
|
|
258
260
|
const content = renderCodeWithComments(
|
|
@@ -262,7 +264,7 @@ export function renderCodePageAsset({
|
|
|
262
264
|
pageDirPath,
|
|
263
265
|
);
|
|
264
266
|
const codeFilePath = applyBasePath(
|
|
265
|
-
normalizeOutputPath(`/${
|
|
267
|
+
normalizeOutputPath(`/${toUrlPath(path.relative(contentDir, filePath))}`),
|
|
266
268
|
);
|
|
267
269
|
const titleHtml = `<tt>${name + ext}</tt>`;
|
|
268
270
|
const tocItems = lang === 'java' ? extractJavaMethodToc(sourceCode) : [];
|
|
@@ -316,7 +318,9 @@ export function renderCopiedContentAsset({
|
|
|
316
318
|
|
|
317
319
|
if (filePath.endsWith('.java') && isFeatureEnabled(siteVariables, 'code')) {
|
|
318
320
|
const sourceCode = fs.readFileSync(filePath, 'utf-8');
|
|
319
|
-
const pageDirPath =
|
|
321
|
+
const pageDirPath = toPosix(
|
|
322
|
+
path.relative(contentDir, path.dirname(filePath)),
|
|
323
|
+
);
|
|
320
324
|
const rewrittenLines = rewriteProseLinks(
|
|
321
325
|
sourceCode.split('\n'),
|
|
322
326
|
siteVariables,
|
|
@@ -501,7 +505,7 @@ export function renderLiterateJavaPageAsset({
|
|
|
501
505
|
}: RenderLiterateJavaOptions): Asset[] {
|
|
502
506
|
const { dir, name } = path.parse(filePath);
|
|
503
507
|
const className = deriveClassName(filePath);
|
|
504
|
-
const subPath = path.relative(contentDir, path.join(dir, className));
|
|
508
|
+
const subPath = toPosix(path.relative(contentDir, path.join(dir, className)));
|
|
505
509
|
const applyBasePath = createApplyBasePath(siteVariables);
|
|
506
510
|
|
|
507
511
|
log.info`Rendering literate Java page ${B`${name}`}`;
|
|
@@ -619,7 +623,7 @@ export function renderLiterateJavaPageAsset({
|
|
|
619
623
|
const javaFileName = `${className}.java`;
|
|
620
624
|
const codeFilePath = applyBasePath(
|
|
621
625
|
normalizeOutputPath(
|
|
622
|
-
`/${
|
|
626
|
+
`/${toUrlPath(path.relative(contentDir, path.join(dir, javaFileName)))}`,
|
|
623
627
|
),
|
|
624
628
|
);
|
|
625
629
|
|
package/build/utils/trace.ts
CHANGED
|
@@ -4,7 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import { execFileSync } from 'child_process';
|
|
5
5
|
import { makeLogger } from '../log';
|
|
6
6
|
import { B } from '../colors';
|
|
7
|
-
import { normalizeOutputPath } from './paths';
|
|
7
|
+
import { normalizeOutputPath, toPosix } from './paths';
|
|
8
8
|
import { hasMainMethod } from './literate-java';
|
|
9
9
|
import { renderCodeSegment } from './code';
|
|
10
10
|
import { computeLayout } from './trace-layout';
|
|
@@ -324,10 +324,7 @@ export function createTraceHelpers(context: TraceContext): {
|
|
|
324
324
|
|
|
325
325
|
const highlightedSource = highlightSource(javaFile, source);
|
|
326
326
|
|
|
327
|
-
const relDir = path
|
|
328
|
-
.relative(contentDir, pageDir)
|
|
329
|
-
.split(path.sep)
|
|
330
|
-
.join(path.posix.sep);
|
|
327
|
+
const relDir = toPosix(path.relative(contentDir, pageDir));
|
|
331
328
|
const traceOutputDir = path.join(distDir, relDir, '_traces', className);
|
|
332
329
|
|
|
333
330
|
const manifest = chunkTraceOutput(
|
|
@@ -69,7 +69,18 @@ function resolveLinkPath(
|
|
|
69
69
|
? normalizePathname(hrefPath)
|
|
70
70
|
: normalizePathname(path.posix.join(sourceDir, hrefPath));
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
// Decode percent-encoded characters so the resolved path can be matched
|
|
73
|
+
// against `validTargets`, which contains raw filesystem-derived paths.
|
|
74
|
+
// markdown-it's normalizeLink percent-encodes hrefs, so a link like
|
|
75
|
+
// `[x](/my notes.md)` arrives here as `/my%20notes.md`.
|
|
76
|
+
let decoded: string;
|
|
77
|
+
try {
|
|
78
|
+
decoded = normalizePathname(decodeURIComponent(resolved));
|
|
79
|
+
} catch {
|
|
80
|
+
decoded = resolved;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return rewriteCodeLink(decoded, codeExtPattern);
|
|
73
84
|
}
|
|
74
85
|
|
|
75
86
|
function getDirectoryIndexPath(pathname: string): string {
|
|
@@ -26,6 +26,7 @@ declare const __WEBSOCKET_PORT__: number;
|
|
|
26
26
|
document.body.classList.add('loading-cursor');
|
|
27
27
|
} else if (event.data === 'reload') {
|
|
28
28
|
console.log('[watch-reload] Reloading page...');
|
|
29
|
+
window.history.scrollRestoration = 'auto';
|
|
29
30
|
window.location.reload();
|
|
30
31
|
}
|
|
31
32
|
};
|
package/build/watch.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
getPublicDir,
|
|
13
13
|
getDistDir,
|
|
14
14
|
getPackageDir,
|
|
15
|
+
toPosix,
|
|
15
16
|
} from './utils/paths';
|
|
16
17
|
import { isFeatureEnabled } from './features';
|
|
17
18
|
import { bundle } from './bundle';
|
|
@@ -55,10 +56,7 @@ export async function runWatch(options: {
|
|
|
55
56
|
define: { __WEBSOCKET_PORT__: String(WEBSOCKET_PORT) },
|
|
56
57
|
});
|
|
57
58
|
return result.outputs.map(output =>
|
|
58
|
-
path
|
|
59
|
-
.relative(getDistDir(), output.path)
|
|
60
|
-
.split(path.sep)
|
|
61
|
-
.join(path.posix.sep),
|
|
59
|
+
toPosix(path.relative(getDistDir(), output.path)),
|
|
62
60
|
);
|
|
63
61
|
}
|
|
64
62
|
|
|
@@ -154,10 +152,7 @@ export async function runWatch(options: {
|
|
|
154
152
|
return null;
|
|
155
153
|
}
|
|
156
154
|
|
|
157
|
-
return path
|
|
158
|
-
.relative(contentDir, normalizedFilePath)
|
|
159
|
-
.split(path.sep)
|
|
160
|
-
.join(path.posix.sep);
|
|
155
|
+
return toPosix(path.relative(contentDir, normalizedFilePath));
|
|
161
156
|
}
|
|
162
157
|
|
|
163
158
|
function toPublicRelativePath(filePath: string): string | null {
|
|
@@ -169,10 +164,7 @@ export async function runWatch(options: {
|
|
|
169
164
|
if (!normalizedFilePath.startsWith(normalizedPublicDir)) {
|
|
170
165
|
return null;
|
|
171
166
|
}
|
|
172
|
-
return path
|
|
173
|
-
.relative(publicDir, normalizedFilePath)
|
|
174
|
-
.split(path.sep)
|
|
175
|
-
.join(path.posix.sep);
|
|
167
|
+
return toPosix(path.relative(publicDir, normalizedFilePath));
|
|
176
168
|
}
|
|
177
169
|
|
|
178
170
|
// Watch mode
|
|
@@ -354,10 +346,7 @@ export async function runWatch(options: {
|
|
|
354
346
|
const absPath = path.resolve(filePath);
|
|
355
347
|
if (fs.existsSync(absPath)) {
|
|
356
348
|
copyPublicFile(publicDir, distDir, absPath, contentAssetRelPaths);
|
|
357
|
-
const rel = path
|
|
358
|
-
.relative(publicDir, absPath)
|
|
359
|
-
.split(path.sep)
|
|
360
|
-
.join(path.posix.sep);
|
|
349
|
+
const rel = toPosix(path.relative(publicDir, absPath));
|
|
361
350
|
publicRelPaths.add(rel);
|
|
362
351
|
}
|
|
363
352
|
}
|
|
@@ -397,10 +386,7 @@ export async function runWatch(options: {
|
|
|
397
386
|
const absPath = path.resolve(filePath);
|
|
398
387
|
if (fs.existsSync(absPath)) {
|
|
399
388
|
copyPublicFile(publicDir, distDir, absPath, contentAssetRelPaths);
|
|
400
|
-
const rel = path
|
|
401
|
-
.relative(publicDir, absPath)
|
|
402
|
-
.split(path.sep)
|
|
403
|
-
.join(path.posix.sep);
|
|
389
|
+
const rel = toPosix(path.relative(publicDir, absPath));
|
|
404
390
|
publicRelPaths.add(rel);
|
|
405
391
|
}
|
|
406
392
|
}
|
|
@@ -417,10 +403,7 @@ export async function runWatch(options: {
|
|
|
417
403
|
const ext = path.extname(absPath).slice(1).toLowerCase();
|
|
418
404
|
if (!processedExtSet.has(ext) && fs.existsSync(absPath)) {
|
|
419
405
|
copyContentFile(contentDir, distDir, absPath, publicRelPaths);
|
|
420
|
-
const rel = path
|
|
421
|
-
.relative(contentDir, absPath)
|
|
422
|
-
.split(path.sep)
|
|
423
|
-
.join(path.posix.sep);
|
|
406
|
+
const rel = toPosix(path.relative(contentDir, absPath));
|
|
424
407
|
contentAssetRelPaths.add(rel);
|
|
425
408
|
}
|
|
426
409
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abreen/tada",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A static site generator",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"prepack": "bun run scripts/prepack.ts",
|
|
30
30
|
"lint": "eslint src/ build/ bin/",
|
|
31
31
|
"typecheck": "tsc --noEmit",
|
|
32
|
-
"test:functional": "
|
|
33
|
-
"test:all": "bun
|
|
34
|
-
"test:coverage": "
|
|
35
|
-
"test:playwright": "
|
|
32
|
+
"test:functional": "bun run scripts/test.ts functional",
|
|
33
|
+
"test:all": "bun run scripts/test.ts all",
|
|
34
|
+
"test:coverage": "bun run scripts/test.ts coverage",
|
|
35
|
+
"test:playwright": "bun run scripts/test.ts playwright",
|
|
36
36
|
"init-example": "bun run bin/tada.ts init example --no-interactive",
|
|
37
37
|
"iew": "if [ -d example ]; then cd example; else bun run init-example && cd example; fi && ../bin/tada.ts watch"
|
|
38
38
|
},
|
|
@@ -41,7 +41,8 @@
|
|
|
41
41
|
},
|
|
42
42
|
"os": [
|
|
43
43
|
"darwin",
|
|
44
|
-
"linux"
|
|
44
|
+
"linux",
|
|
45
|
+
"win32"
|
|
45
46
|
],
|
|
46
47
|
"dependencies": {
|
|
47
48
|
"@vscode/markdown-it-katex": "^1.1.2",
|
|
@@ -77,7 +78,7 @@
|
|
|
77
78
|
},
|
|
78
79
|
"devDependencies": {
|
|
79
80
|
"@eslint/js": "^10.0.1",
|
|
80
|
-
"@playwright/test": "
|
|
81
|
+
"@playwright/test": "1.56.1",
|
|
81
82
|
"@saintedlama/lcov-parse": "^1.0.1",
|
|
82
83
|
"@types/bun": "^1.3.10",
|
|
83
84
|
"@types/culori": "^4.0.1",
|