tessera-learn 0.2.2 → 0.3.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/AGENTS.md +161 -535
- package/README.md +2 -2
- package/dist/{audit-B9VHgVjk.js → audit-DkXqQTqn.js} +92 -38
- package/dist/audit-DkXqQTqn.js.map +1 -0
- package/dist/{build-commands-D127jw0J.js → build-commands-CyzuCDXg.js} +2 -2
- package/dist/{build-commands-D127jw0J.js.map → build-commands-CyzuCDXg.js.map} +1 -1
- package/dist/{inline-config-eHjv9XuA.js → inline-config-BEXyRqsJ.js} +2 -2
- package/dist/{inline-config-eHjv9XuA.js.map → inline-config-BEXyRqsJ.js.map} +1 -1
- package/dist/plugin/cli.d.ts.map +1 -1
- package/dist/plugin/cli.js +62 -54
- package/dist/plugin/cli.js.map +1 -1
- package/dist/plugin/index.d.ts +280 -3
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +3 -3
- package/dist/{plugin--8H9xQIl.js → plugin-CFUFgwHB.js} +126 -83
- package/dist/plugin-CFUFgwHB.js.map +1 -0
- package/package.json +12 -9
- package/src/components/DefaultLayout.svelte +2 -5
- package/src/components/Quiz.svelte +18 -26
- package/src/plugin/a11y/audit.ts +8 -13
- package/src/plugin/a11y-cli.ts +1 -4
- package/src/plugin/ast.ts +9 -2
- package/src/plugin/cli.ts +46 -48
- package/src/plugin/csp.ts +59 -0
- package/src/plugin/duplicate-cli.ts +37 -1
- package/src/plugin/export.ts +56 -27
- package/src/plugin/index.ts +117 -61
- package/src/plugin/manifest.ts +3 -23
- package/src/plugin/new-cli.ts +2 -0
- package/src/plugin/validate-cli.ts +10 -4
- package/src/plugin/validation.ts +48 -12
- package/src/runtime/App.svelte +10 -8
- package/src/runtime/Sidebar.svelte +3 -1
- package/src/runtime/adapters/cmi5.ts +59 -402
- package/src/runtime/adapters/discovery.ts +11 -0
- package/src/runtime/adapters/index.ts +27 -60
- package/src/runtime/adapters/lms-error.ts +61 -0
- package/src/runtime/adapters/scorm2004.ts +2 -1
- package/src/runtime/adapters/web.ts +19 -4
- package/src/runtime/adapters/xapi-launch-base.ts +346 -0
- package/src/runtime/adapters/xapi.ts +26 -0
- package/src/runtime/types.ts +19 -1
- package/src/runtime/xapi/publisher.ts +5 -1
- package/src/runtime/xapi/setup.ts +24 -15
- package/src/virtual.d.ts +4 -1
- package/templates/course/course.config.js +1 -0
- package/dist/audit-B9VHgVjk.js.map +0 -1
- package/dist/plugin--8H9xQIl.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# tessera-learn
|
|
2
2
|
|
|
3
|
-
LMS tracking runtime for interactive learning content. One adapter layer (SCORM 1.2, SCORM 2004 4th Edition, cmi5, static Web), your choice of components.
|
|
3
|
+
LMS tracking runtime for interactive learning content. One adapter layer (SCORM 1.2, SCORM 2004 4th Edition, cmi5, xAPI 1.0.3, static Web), your choice of components.
|
|
4
4
|
|
|
5
5
|
Tessera is a toolkit for building interactive online courses, designed for AI-assisted authoring: pages are `.svelte` files, the runtime locks the LMS data contract (tracking, completion, scoring, persistence), and an AI agent working in the workspace follows the conventions in `AGENTS.md` to write pages and components. This package is the runtime; you typically don't depend on it directly — `create-tessera` scaffolds a workspace that pins it for you. A workspace is one package that holds many courses under `courses/<name>/` and a `shared/` design system imported as `$shared`.
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ That creates a workspace with Tessera wired up, a seed course, and a root `AGENT
|
|
|
19
19
|
- **Hooks** (`tessera-learn`): `useQuestion`, `useQuiz`, `useNavigation`, `useProgress`, `useCompletion`, `usePersistence`, `useXAPI`.
|
|
20
20
|
- **Vite plugin** (`tessera-learn/plugin`): `tesseraPlugin()` — wires page/layout discovery, the LMS adapter, the `$shared` alias, and the export pipeline. The `tessera` CLI (`new`/`dev`/`export`/`validate`/`a11y`/`check`) runs Vite with this plugin for you, so scaffolded workspaces need no `vite.config.js`.
|
|
21
21
|
- **Built-in components** (`tessera-learn`): `Callout`, `Image`, `Audio`, `Video`, `Accordion` / `AccordionItem`, `Carousel` / `CarouselSlide`, `RevealModal`, `Quiz`, `MultipleChoice`, `FillInTheBlank`, `Matching`, `Sorting`, `DefaultLayout`.
|
|
22
|
-
- **LMS adapters**: SCORM 1.2, SCORM 2004 4th Edition, cmi5, static Web — selected via `course.config.js` `export.standard`.
|
|
22
|
+
- **LMS adapters**: SCORM 1.2, SCORM 2004 4th Edition, cmi5, xAPI 1.0.3 ("Tin Can"), static Web — selected via `course.config.js` `export.standard`.
|
|
23
23
|
- **Accessibility checks**: static rules (alt text, media titles/captions, heading order, contrast, `<html lang>`) run inside validation and the build with zero extra dependencies; an opt-in runtime audit (`tessera a11y`, with `playwright` + `@axe-core/playwright` as optional peers) renders every page and gates on axe-core violations.
|
|
24
24
|
|
|
25
25
|
See `AGENTS.md` for usage, signatures, and authoring conventions.
|
|
@@ -8,9 +8,11 @@ import { parse } from "svelte/compiler";
|
|
|
8
8
|
import { spawn } from "node:child_process";
|
|
9
9
|
//#region src/plugin/ast.ts
|
|
10
10
|
const rootCache = /* @__PURE__ */ new Map();
|
|
11
|
+
const jsModuleCache = /* @__PURE__ */ new Map();
|
|
11
12
|
/** Drop every cached root. Call at the start of a run to scope the cache. */
|
|
12
13
|
function clearParseCache() {
|
|
13
14
|
rootCache.clear();
|
|
15
|
+
jsModuleCache.clear();
|
|
14
16
|
}
|
|
15
17
|
function parseRoot(source) {
|
|
16
18
|
const cached = rootCache.get(source);
|
|
@@ -119,14 +121,19 @@ function findComponents(source, names) {
|
|
|
119
121
|
}
|
|
120
122
|
const TsParser = Parser.extend(tsPlugin());
|
|
121
123
|
function parseJsModule(source) {
|
|
124
|
+
const cached = jsModuleCache.get(source);
|
|
125
|
+
if (cached !== void 0) return cached;
|
|
126
|
+
let result;
|
|
122
127
|
try {
|
|
123
|
-
|
|
128
|
+
result = TsParser.parse(source, {
|
|
124
129
|
ecmaVersion: "latest",
|
|
125
130
|
sourceType: "module"
|
|
126
131
|
});
|
|
127
132
|
} catch {
|
|
128
|
-
|
|
133
|
+
result = null;
|
|
129
134
|
}
|
|
135
|
+
jsModuleCache.set(source, result);
|
|
136
|
+
return result;
|
|
130
137
|
}
|
|
131
138
|
function unwrapTsCast(node) {
|
|
132
139
|
let current = node;
|
|
@@ -240,15 +247,6 @@ function deriveSlug(name, isFile = false) {
|
|
|
240
247
|
return stripPrefix(name);
|
|
241
248
|
}
|
|
242
249
|
/**
|
|
243
|
-
* Locate `export default { ... }` and return its object-literal text. Returns
|
|
244
|
-
* a discriminated result so callers can tell parse failure from a missing or
|
|
245
|
-
* non-literal default export. Used by both manifest extraction and project
|
|
246
|
-
* validation.
|
|
247
|
-
*/
|
|
248
|
-
function extractDefaultExportObjectLiteral(source) {
|
|
249
|
-
return defaultExportObjectLiteral(source);
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
250
|
* Read and JSON5-parse the `export default { ... }` literal from a project's
|
|
253
251
|
* course.config.js. Shared by the build plugin and the validator so the read,
|
|
254
252
|
* cache, and parse rules live in one place. The discriminated `reason` lets
|
|
@@ -261,7 +259,7 @@ function readCourseConfig(projectRoot) {
|
|
|
261
259
|
ok: false,
|
|
262
260
|
reason: "missing"
|
|
263
261
|
};
|
|
264
|
-
const result =
|
|
262
|
+
const result = defaultExportObjectLiteral(readSourceFileCached(configPath));
|
|
265
263
|
if (result.kind === "parse-error") return {
|
|
266
264
|
ok: false,
|
|
267
265
|
reason: "parse-error"
|
|
@@ -290,7 +288,7 @@ function readCourseConfig(projectRoot) {
|
|
|
290
288
|
*/
|
|
291
289
|
function readMetaFile(metaPath) {
|
|
292
290
|
if (!existsSync(metaPath)) return {};
|
|
293
|
-
const result =
|
|
291
|
+
const result = defaultExportObjectLiteral(readSourceFileCached(metaPath));
|
|
294
292
|
if (result.kind !== "literal") return {};
|
|
295
293
|
try {
|
|
296
294
|
return JSON5.parse(result.text);
|
|
@@ -548,6 +546,14 @@ const FEEDBACK_MODES = [
|
|
|
548
546
|
"never"
|
|
549
547
|
];
|
|
550
548
|
const RETRY_MODES = ["full", "incorrect-only"];
|
|
549
|
+
/**
|
|
550
|
+
* Trimmed course identity, or '' when absent. Single source of truth for the
|
|
551
|
+
* "is there a usable id?" check shared by the web storage key, the cmi5/xAPI
|
|
552
|
+
* id derivation, and the config validator.
|
|
553
|
+
*/
|
|
554
|
+
function courseIdentity(config) {
|
|
555
|
+
return typeof config.id === "string" && config.id.trim() || "";
|
|
556
|
+
}
|
|
551
557
|
//#endregion
|
|
552
558
|
//#region src/plugin/a11y/contrast.ts
|
|
553
559
|
/**
|
|
@@ -606,6 +612,48 @@ function contrastRatio(a, b) {
|
|
|
606
612
|
return (lighter + .05) / (darker + .05);
|
|
607
613
|
}
|
|
608
614
|
//#endregion
|
|
615
|
+
//#region src/plugin/csp.ts
|
|
616
|
+
const WEB_CSP_BASELINE = {
|
|
617
|
+
"default-src": ["'self'"],
|
|
618
|
+
"img-src": [
|
|
619
|
+
"'self'",
|
|
620
|
+
"data:",
|
|
621
|
+
"https:"
|
|
622
|
+
],
|
|
623
|
+
"media-src": [
|
|
624
|
+
"'self'",
|
|
625
|
+
"blob:",
|
|
626
|
+
"data:",
|
|
627
|
+
"https:"
|
|
628
|
+
],
|
|
629
|
+
"style-src": ["'self'", "'unsafe-inline'"],
|
|
630
|
+
"script-src": ["'self'", "'unsafe-inline'"],
|
|
631
|
+
"font-src": ["'self'", "data:"],
|
|
632
|
+
"connect-src": ["'self'", "https:"],
|
|
633
|
+
"frame-src": [
|
|
634
|
+
"'self'",
|
|
635
|
+
"blob:",
|
|
636
|
+
"https:"
|
|
637
|
+
],
|
|
638
|
+
"worker-src": ["'self'", "blob:"],
|
|
639
|
+
"object-src": ["'none'"],
|
|
640
|
+
"base-uri": ["'self'"]
|
|
641
|
+
};
|
|
642
|
+
const CSP_DIRECTIVE = /^[a-zA-Z][a-zA-Z-]*$/;
|
|
643
|
+
const CSP_SOURCE = /^[^\s;,"<>]+$/;
|
|
644
|
+
function isCspOverrides(v) {
|
|
645
|
+
return typeof v === "object" && v !== null && !Array.isArray(v) && Object.entries(v).every(([directive, sources]) => CSP_DIRECTIVE.test(directive) && Array.isArray(sources) && sources.every((s) => typeof s === "string" && CSP_SOURCE.test(s)));
|
|
646
|
+
}
|
|
647
|
+
function buildCsp(overrides) {
|
|
648
|
+
const merged = new Map(Object.entries(WEB_CSP_BASELINE).map(([k, v]) => [k, [...v]]));
|
|
649
|
+
if (isCspOverrides(overrides)) for (const [directive, sources] of Object.entries(overrides)) {
|
|
650
|
+
const existing = merged.get(directive) ?? [];
|
|
651
|
+
for (const src of sources) if (!existing.includes(src)) existing.push(src);
|
|
652
|
+
merged.set(directive, existing);
|
|
653
|
+
}
|
|
654
|
+
return [...merged].map(([directive, sources]) => `${directive} ${sources.join(" ")}`).join("; ");
|
|
655
|
+
}
|
|
656
|
+
//#endregion
|
|
609
657
|
//#region src/components/video-embed.ts
|
|
610
658
|
/**
|
|
611
659
|
* Shared YouTube/Vimeo embed detection. Used by Video.svelte to pick the iframe
|
|
@@ -640,7 +688,7 @@ const A11Y_IDS = {
|
|
|
640
688
|
lang: "tessera/lang"
|
|
641
689
|
};
|
|
642
690
|
/** Promotable by `a11y.level: 'error'`; the rest are hard contract errors. */
|
|
643
|
-
const PROMOTABLE_A11Y_IDS = new Set([
|
|
691
|
+
const PROMOTABLE_A11Y_IDS = /* @__PURE__ */ new Set([
|
|
644
692
|
A11Y_IDS.mediaTranscript,
|
|
645
693
|
A11Y_IDS.mediaCaptions,
|
|
646
694
|
A11Y_IDS.questionLabel,
|
|
@@ -703,8 +751,9 @@ function reportValidationIssues({ errors, warnings }) {
|
|
|
703
751
|
for (const warning of warnings) console.warn(`\x1b[33m[tessera warning]\x1b[0m ${warning}`);
|
|
704
752
|
for (const error of errors) console.error(`\x1b[31m[tessera error]\x1b[0m ${error}`);
|
|
705
753
|
}
|
|
706
|
-
const KNOWN_CONFIG_FIELDS = new Set([
|
|
754
|
+
const KNOWN_CONFIG_FIELDS = /* @__PURE__ */ new Set([
|
|
707
755
|
"title",
|
|
756
|
+
"id",
|
|
708
757
|
"description",
|
|
709
758
|
"author",
|
|
710
759
|
"version",
|
|
@@ -733,7 +782,8 @@ const VALID_EXPORT_STANDARDS = [
|
|
|
733
782
|
"web",
|
|
734
783
|
"scorm12",
|
|
735
784
|
"scorm2004",
|
|
736
|
-
"cmi5"
|
|
785
|
+
"cmi5",
|
|
786
|
+
"xapi"
|
|
737
787
|
];
|
|
738
788
|
const VALID_MANUAL_TRIGGERS = ["page"];
|
|
739
789
|
const VALID_REQUIRE_SUCCESS_STATUS = ["passed", "failed"];
|
|
@@ -785,6 +835,8 @@ function parseConfig(projectRoot, errors, warnings) {
|
|
|
785
835
|
if (config.branding !== void 0) validateBranding(config.branding, warnings);
|
|
786
836
|
if (config.language === void 0) warnings.push(tag(A11Y_IDS.lang, `course.config.js: "language" is not set — defaulting <html lang> to "en". Set it to the course's language (BCP-47, e.g. "en", "fr-CA") for WCAG 3.1.1.`));
|
|
787
837
|
else if (!isPlausibleLanguageTag(config.language)) warnings.push(tag(A11Y_IDS.lang, `course.config.js: "language" (${JSON.stringify(config.language)}) is not a plausible BCP-47 tag — use e.g. "en", "es", or "fr-CA"`));
|
|
838
|
+
const standard = config.export?.standard;
|
|
839
|
+
if ((standard === void 0 || standard === "web" || standard === "cmi5" || standard === "xapi") && !courseIdentity(config)) warnings.push(`course.config.js: no "id" set — the web storage key and cmi5/xAPI activity id then share a fixed fallback that collides across courses. Add a unique id (e.g. "urn:uuid:…"); scaffolded courses include one.`);
|
|
788
840
|
if (config.a11y !== void 0) validateA11yConfig(config.a11y, errors);
|
|
789
841
|
if (config.navigation?.mode !== void 0) {
|
|
790
842
|
if (!VALID_NAV_MODES.includes(config.navigation.mode)) errors.push(`course.config.js: "navigation.mode" must be "free" or "sequential", got "${config.navigation.mode}"`);
|
|
@@ -801,7 +853,12 @@ function parseConfig(projectRoot, errors, warnings) {
|
|
|
801
853
|
else if (!VALID_REQUIRE_SUCCESS_STATUS.includes(config.completion.requireSuccessStatus)) errors.push(`course.config.js: "completion.requireSuccessStatus" must be "passed" or "failed" (omit for "unknown"), got "${config.completion.requireSuccessStatus}"`);
|
|
802
854
|
}
|
|
803
855
|
if (config.export?.standard !== void 0) {
|
|
804
|
-
if (!VALID_EXPORT_STANDARDS.includes(config.export.standard)) errors.push(`course.config.js: "export.standard" must be "web", "scorm12", "scorm2004", or "
|
|
856
|
+
if (!VALID_EXPORT_STANDARDS.includes(config.export.standard)) errors.push(`course.config.js: "export.standard" must be "web", "scorm12", "scorm2004", "cmi5", or "xapi", got "${config.export.standard}"`);
|
|
857
|
+
}
|
|
858
|
+
if (config.export?.csp !== void 0) {
|
|
859
|
+
const csp = config.export.csp;
|
|
860
|
+
if (csp !== false && !isCspOverrides(csp)) warnings.push("course.config.js: \"export.csp\" must be false or an object of directive → string[]; ignoring it and using the baseline CSP");
|
|
861
|
+
else if ((config.export.standard ?? "web") !== "web") warnings.push(`course.config.js: "export.csp" is ignored when "export.standard" is "${config.export.standard}" (the CSP meta is web-export only)`);
|
|
805
862
|
}
|
|
806
863
|
if (config.scoring?.passingScore !== void 0) {
|
|
807
864
|
const score = config.scoring.passingScore;
|
|
@@ -872,7 +929,7 @@ function validateXAPIConfig(raw, standard, errors, warnings) {
|
|
|
872
929
|
errors.push("course.config.js: xapi must contain at least one destination, or be omitted");
|
|
873
930
|
return;
|
|
874
931
|
}
|
|
875
|
-
if (entries.filter((e) => e && typeof e === "object" && e.endpoint === "lms").length > 1) errors.push("course.config.js: xapi has multiple entries with endpoint: 'lms' — only one
|
|
932
|
+
if (entries.filter((e) => e && typeof e === "object" && e.endpoint === "lms").length > 1) errors.push("course.config.js: xapi has multiple entries with endpoint: 'lms' — only one launch-inherited destination is allowed");
|
|
876
933
|
const seen = /* @__PURE__ */ new Map();
|
|
877
934
|
for (const e of entries) if (e && typeof e === "object") {
|
|
878
935
|
const ep = e.endpoint;
|
|
@@ -904,14 +961,14 @@ function validateSingleXAPIEntry(entry, label, standard, errors, warnings) {
|
|
|
904
961
|
return;
|
|
905
962
|
}
|
|
906
963
|
if (endpoint === "lms") {
|
|
907
|
-
if (standard !== "cmi5") errors.push(`course.config.js: ${label}.endpoint: 'lms' requires export.standard: 'cmi5' (you have "${standard}"). Either change the export standard or specify an explicit LRS endpoint.`);
|
|
964
|
+
if (standard !== "cmi5" && standard !== "xapi") errors.push(`course.config.js: ${label}.endpoint: 'lms' requires export.standard: 'cmi5' or 'xapi' (you have "${standard}"). Either change the export standard or specify an explicit LRS endpoint.`);
|
|
908
965
|
for (const f of [
|
|
909
966
|
"auth",
|
|
910
967
|
"actor",
|
|
911
968
|
"activityId",
|
|
912
969
|
"registration",
|
|
913
970
|
"actorAccountHomePage"
|
|
914
|
-
]) if (entry[f] !== void 0) errors.push(`course.config.js: ${label}.${f} must be omitted when ${label}.endpoint is 'lms' — it is inherited from the
|
|
971
|
+
]) if (entry[f] !== void 0) errors.push(`course.config.js: ${label}.${f} must be omitted when ${label}.endpoint is 'lms' — it is inherited from the launch.`);
|
|
915
972
|
return;
|
|
916
973
|
}
|
|
917
974
|
let url;
|
|
@@ -958,13 +1015,13 @@ function validateSingleXAPIEntry(entry, label, standard, errors, warnings) {
|
|
|
958
1015
|
errors.push(`course.config.js: ${label}.actorAccountHomePage must be an absolute URL`);
|
|
959
1016
|
}
|
|
960
1017
|
if (actor !== void 0) warnings.push(`course.config.js: ${label}.actorAccountHomePage is ignored when ${label}.actor is supplied explicitly.`);
|
|
961
|
-
if (standard === "cmi5" || standard === "web") warnings.push(`course.config.js: ${label}.actorAccountHomePage is only used under scorm12/scorm2004 actor synthesis; ignored under "${standard}".`);
|
|
1018
|
+
if (standard === "cmi5" || standard === "xapi" || standard === "web") warnings.push(`course.config.js: ${label}.actorAccountHomePage is only used under scorm12/scorm2004 actor synthesis; ignored under "${standard}".`);
|
|
962
1019
|
}
|
|
963
1020
|
if (actor === void 0 && (standard === "scorm12" || standard === "scorm2004") && typeof activityId === "string" && httpOrigin(activityId) === null && aahp === void 0) errors.push(`course.config.js: ${label}.activityId is not an http(s) URL, so its origin can't be used as the SCORM actor's account.homePage. Provide ${label}.actorAccountHomePage explicitly.`);
|
|
964
1021
|
const registration = entry.registration;
|
|
965
1022
|
if (registration !== void 0) {
|
|
966
1023
|
if (typeof registration !== "string" || !UUID_RE.test(registration)) errors.push(`course.config.js: ${label}.registration must be a UUID v4, got "${String(registration)}"`);
|
|
967
|
-
if (standard !== "cmi5") warnings.push(`course.config.js: ${label}.registration is a cmi5 concept; the LRS will accept it under "${standard}" but most analytics tools won't know what to do with it.`);
|
|
1024
|
+
if (standard !== "cmi5" && standard !== "xapi") warnings.push(`course.config.js: ${label}.registration is a cmi5 concept; the LRS will accept it under "${standard}" but most analytics tools won't know what to do with it.`);
|
|
968
1025
|
}
|
|
969
1026
|
}
|
|
970
1027
|
/**
|
|
@@ -1086,7 +1143,7 @@ function validatePages(pagesDir, assetsDir, projectRoot, exportStandard) {
|
|
|
1086
1143
|
function validateMetaFile(metaPath, parentRel, errors) {
|
|
1087
1144
|
if (!existsSync(metaPath)) return null;
|
|
1088
1145
|
const metaRel = `${parentRel}/_meta.js`;
|
|
1089
|
-
const result =
|
|
1146
|
+
const result = defaultExportObjectLiteral(readSourceFileCached(metaPath));
|
|
1090
1147
|
if (result.kind === "parse-error") {
|
|
1091
1148
|
errors.push(`${metaRel}: could not parse — JavaScript syntax error`);
|
|
1092
1149
|
return null;
|
|
@@ -1242,7 +1299,7 @@ function stripRepeated(input, patterns) {
|
|
|
1242
1299
|
* Non-static (kind 'expr') values are skipped, matching the rest of the linter.
|
|
1243
1300
|
*/
|
|
1244
1301
|
function validateMediaComponents(content, fileRel, errors, warnings) {
|
|
1245
|
-
const components = findComponents(content, new Set([
|
|
1302
|
+
const components = findComponents(content, /* @__PURE__ */ new Set([
|
|
1246
1303
|
"Image",
|
|
1247
1304
|
"Video",
|
|
1248
1305
|
"Audio"
|
|
@@ -1574,27 +1631,24 @@ async function runAudit(projectRoot, workspaceRoot, options = {}) {
|
|
|
1574
1631
|
const disableRules = axeIgnoreRules(settings.ignore);
|
|
1575
1632
|
const manifest = generateManifest(resolve(projectRoot, "pages"));
|
|
1576
1633
|
const vite = await import("vite");
|
|
1577
|
-
const { resolveTesseraConfig } = await import("./inline-config-
|
|
1634
|
+
const { resolveTesseraConfig } = await import("./inline-config-BEXyRqsJ.js");
|
|
1578
1635
|
const auditBaseConfig = await resolveTesseraConfig(projectRoot, workspaceRoot, {
|
|
1579
1636
|
command: "build",
|
|
1580
1637
|
mode: "production"
|
|
1581
1638
|
});
|
|
1582
1639
|
const auditDist = resolve(projectRoot, "node_modules", ".tessera-a11y");
|
|
1583
|
-
const distHtml = resolve(auditDist, "index.html");
|
|
1584
1640
|
const prevEnv = process.env[AUDIT_ENV_FLAG];
|
|
1585
1641
|
process.env[AUDIT_ENV_FLAG] = "1";
|
|
1586
1642
|
let server;
|
|
1587
1643
|
try {
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
}));
|
|
1597
|
-
}
|
|
1644
|
+
console.log("[tessera a11y] Building course…");
|
|
1645
|
+
await vite.build(vite.mergeConfig(auditBaseConfig, {
|
|
1646
|
+
build: {
|
|
1647
|
+
outDir: auditDist,
|
|
1648
|
+
emptyOutDir: true
|
|
1649
|
+
},
|
|
1650
|
+
logLevel: "warn"
|
|
1651
|
+
}));
|
|
1598
1652
|
server = await vite.preview({
|
|
1599
1653
|
root: projectRoot,
|
|
1600
1654
|
base: auditBaseConfig.base,
|
|
@@ -1728,6 +1782,6 @@ function printSummary(report, reportPath) {
|
|
|
1728
1782
|
}
|
|
1729
1783
|
}
|
|
1730
1784
|
//#endregion
|
|
1731
|
-
export { isPlausibleLanguageTag as a, validateProject as c, isIgnored as i,
|
|
1785
|
+
export { isPlausibleLanguageTag as a, validateProject as c, generateManifest as d, readCourseConfig as f, isIgnored as i, buildCsp as l, runAudit as n, normalizeA11y as o, resolvePackageRoot as r, reportValidationIssues as s, AUDIT_ENV_FLAG as t, courseIdentity as u };
|
|
1732
1786
|
|
|
1733
|
-
//# sourceMappingURL=audit-
|
|
1787
|
+
//# sourceMappingURL=audit-DkXqQTqn.js.map
|