coursecode 0.1.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/LICENSE +21 -0
- package/README.md +322 -0
- package/THIRD_PARTY_NOTICES.md +22 -0
- package/bin/cli.js +331 -0
- package/framework/assets/logo-coursecode-black.svg +14 -0
- package/framework/assets/logo-coursecode-white.svg +14 -0
- package/framework/assets/logo-coursecode.svg +14 -0
- package/framework/css/01-base.css +160 -0
- package/framework/css/02-layout.css +499 -0
- package/framework/css/accessibility.css +834 -0
- package/framework/css/components/accordions.css +710 -0
- package/framework/css/components/assessments.css +520 -0
- package/framework/css/components/audio-player.css +570 -0
- package/framework/css/components/badges.css +80 -0
- package/framework/css/components/breadcrumbs.css +87 -0
- package/framework/css/components/buttons.css +707 -0
- package/framework/css/components/callouts.css +1280 -0
- package/framework/css/components/cards.css +475 -0
- package/framework/css/components/carousel.css +193 -0
- package/framework/css/components/checkbox-group.css +123 -0
- package/framework/css/components/checklist.css +203 -0
- package/framework/css/components/collapse.css +96 -0
- package/framework/css/components/comparison.css +33 -0
- package/framework/css/components/content-image.css +36 -0
- package/framework/css/components/document-gallery.css +425 -0
- package/framework/css/components/dropdown.css +115 -0
- package/framework/css/components/embed-frame.css +142 -0
- package/framework/css/components/engagement.css +412 -0
- package/framework/css/components/features.css +35 -0
- package/framework/css/components/flip-cards.css +253 -0
- package/framework/css/components/footer.css +353 -0
- package/framework/css/components/forms.css +294 -0
- package/framework/css/components/hero.css +216 -0
- package/framework/css/components/images.css +528 -0
- package/framework/css/components/interactive-timeline.css +274 -0
- package/framework/css/components/intro-cards.css +30 -0
- package/framework/css/components/lightbox.css +666 -0
- package/framework/css/components/loading.css +65 -0
- package/framework/css/components/modals.css +235 -0
- package/framework/css/components/notifications.css +107 -0
- package/framework/css/components/quote.css +150 -0
- package/framework/css/components/sidebar.css +684 -0
- package/framework/css/components/slide-header.css +52 -0
- package/framework/css/components/spinner.css +62 -0
- package/framework/css/components/stats.css +44 -0
- package/framework/css/components/steps.css +232 -0
- package/framework/css/components/tables.css +90 -0
- package/framework/css/components/tabs.css +347 -0
- package/framework/css/components/timeline.css +154 -0
- package/framework/css/components/toggle.css +95 -0
- package/framework/css/components/tooltip.css +226 -0
- package/framework/css/components/video-player.css +438 -0
- package/framework/css/design-tokens.css +707 -0
- package/framework/css/framework.css +86 -0
- package/framework/css/interactions/accessibility.css +75 -0
- package/framework/css/interactions/base.css +92 -0
- package/framework/css/interactions/drag-drop.css +295 -0
- package/framework/css/interactions/fill-in-the-blank.css +236 -0
- package/framework/css/interactions/hotspots.css +69 -0
- package/framework/css/interactions/index.css +45 -0
- package/framework/css/interactions/interactive-image.css +359 -0
- package/framework/css/interactions/likert.css +126 -0
- package/framework/css/interactions/matching.css +354 -0
- package/framework/css/interactions/numeric-input.css +78 -0
- package/framework/css/interactions/sequencing.css +378 -0
- package/framework/css/interactions/true-false.css +177 -0
- package/framework/css/layouts/article.css +258 -0
- package/framework/css/layouts/base.css +30 -0
- package/framework/css/layouts/canvas.css +38 -0
- package/framework/css/layouts/focused.css +236 -0
- package/framework/css/layouts/index.css +29 -0
- package/framework/css/layouts/presentation.css +191 -0
- package/framework/css/layouts/traditional.css +52 -0
- package/framework/css/responsive.css +439 -0
- package/framework/css/utilities/accessibility-utils.css +59 -0
- package/framework/css/utilities/animations.css +419 -0
- package/framework/css/utilities/borders.css +72 -0
- package/framework/css/utilities/colors.css +76 -0
- package/framework/css/utilities/container.css +46 -0
- package/framework/css/utilities/decorative.css +442 -0
- package/framework/css/utilities/display.css +257 -0
- package/framework/css/utilities/flexbox.css +80 -0
- package/framework/css/utilities/grid.css +69 -0
- package/framework/css/utilities/icons.css +534 -0
- package/framework/css/utilities/lists.css +190 -0
- package/framework/css/utilities/spacing.css +167 -0
- package/framework/css/utilities/tables.css +81 -0
- package/framework/css/utilities/typography.css +159 -0
- package/framework/css/utilities/visibility.css +117 -0
- package/framework/docs/COURSE_AUTHORING_GUIDE.md +1773 -0
- package/framework/docs/COURSE_OUTLINE_GUIDE.md +725 -0
- package/framework/docs/COURSE_OUTLINE_TEMPLATE.md +161 -0
- package/framework/docs/DATA_MODEL.md +409 -0
- package/framework/docs/FRAMEWORK_GUIDE.md +1088 -0
- package/framework/docs/USER_GUIDE.md +583 -0
- package/framework/docs/examples/cloudflare-channel-relay.js +169 -0
- package/framework/docs/examples/cloudflare-data-worker.js +102 -0
- package/framework/docs/examples/cloudflare-error-worker.js +228 -0
- package/framework/index.html +175 -0
- package/framework/js/app/AppActions.js +410 -0
- package/framework/js/app/AppState.js +225 -0
- package/framework/js/app/AppUI.js +616 -0
- package/framework/js/assessment/AssessmentActions.js +615 -0
- package/framework/js/assessment/AssessmentFactory.js +471 -0
- package/framework/js/assessment/AssessmentState.js +322 -0
- package/framework/js/assessment/AssessmentUI.js +451 -0
- package/framework/js/automation/api-engagement.js +196 -0
- package/framework/js/automation/api-interactions.js +167 -0
- package/framework/js/automation/api.js +242 -0
- package/framework/js/automation/index.js +41 -0
- package/framework/js/components/interactions/drag-drop.js +884 -0
- package/framework/js/components/interactions/fill-in.js +535 -0
- package/framework/js/components/interactions/hotspot.js +702 -0
- package/framework/js/components/interactions/interaction-base.js +511 -0
- package/framework/js/components/interactions/likert.js +301 -0
- package/framework/js/components/interactions/matching.js +699 -0
- package/framework/js/components/interactions/multiple-choice.js +377 -0
- package/framework/js/components/interactions/numeric.js +271 -0
- package/framework/js/components/interactions/sequencing.js +423 -0
- package/framework/js/components/interactions/true-false.js +241 -0
- package/framework/js/components/ui-components/accordion.js +442 -0
- package/framework/js/components/ui-components/alert.js +88 -0
- package/framework/js/components/ui-components/audio-player.js +1193 -0
- package/framework/js/components/ui-components/callout.js +121 -0
- package/framework/js/components/ui-components/carousel.js +145 -0
- package/framework/js/components/ui-components/checkbox-group.js +87 -0
- package/framework/js/components/ui-components/checklist.js +40 -0
- package/framework/js/components/ui-components/collapse.js +114 -0
- package/framework/js/components/ui-components/comparison.js +30 -0
- package/framework/js/components/ui-components/conditional-display.js +150 -0
- package/framework/js/components/ui-components/content-image.js +41 -0
- package/framework/js/components/ui-components/dropdown.js +262 -0
- package/framework/js/components/ui-components/embed-frame.js +274 -0
- package/framework/js/components/ui-components/features.js +33 -0
- package/framework/js/components/ui-components/flip-card.js +230 -0
- package/framework/js/components/ui-components/form-validator.js +76 -0
- package/framework/js/components/ui-components/hero.js +49 -0
- package/framework/js/components/ui-components/index.js +12 -0
- package/framework/js/components/ui-components/interactive-image.js +235 -0
- package/framework/js/components/ui-components/interactive-timeline.js +285 -0
- package/framework/js/components/ui-components/intro-cards.js +35 -0
- package/framework/js/components/ui-components/lightbox.js +652 -0
- package/framework/js/components/ui-components/modal.js +386 -0
- package/framework/js/components/ui-components/notifications.js +145 -0
- package/framework/js/components/ui-components/progress.js +88 -0
- package/framework/js/components/ui-components/quote.js +41 -0
- package/framework/js/components/ui-components/stats.js +33 -0
- package/framework/js/components/ui-components/steps.js +41 -0
- package/framework/js/components/ui-components/tabs.js +255 -0
- package/framework/js/components/ui-components/timeline.js +42 -0
- package/framework/js/components/ui-components/toggle-group.js +73 -0
- package/framework/js/components/ui-components/tooltip.js +458 -0
- package/framework/js/components/ui-components/value-display.js +133 -0
- package/framework/js/components/ui-components/video-player.js +686 -0
- package/framework/js/core/component-catalog.js +121 -0
- package/framework/js/core/event-bus.js +178 -0
- package/framework/js/core/interaction-catalog.js +149 -0
- package/framework/js/dev/runtime-linter.js +1725 -0
- package/framework/js/drivers/cmi5-driver.js +768 -0
- package/framework/js/drivers/driver-factory.js +77 -0
- package/framework/js/drivers/driver-interface.js +110 -0
- package/framework/js/drivers/http-driver-base.js +241 -0
- package/framework/js/drivers/lti-driver.js +508 -0
- package/framework/js/drivers/proxy-driver.js +444 -0
- package/framework/js/drivers/scorm-12-driver.js +560 -0
- package/framework/js/drivers/scorm-2004-driver.js +775 -0
- package/framework/js/drivers/scorm-driver-base.js +112 -0
- package/framework/js/engagement/engagement-manager.js +404 -0
- package/framework/js/engagement/engagement-progress.js +191 -0
- package/framework/js/engagement/engagement-trackers.js +215 -0
- package/framework/js/engagement/requirement-strategies.js +268 -0
- package/framework/js/main.js +727 -0
- package/framework/js/managers/accessibility-manager.js +499 -0
- package/framework/js/managers/assessment-manager.js +230 -0
- package/framework/js/managers/audio-manager.js +944 -0
- package/framework/js/managers/comment-manager.js +88 -0
- package/framework/js/managers/flag-manager.js +86 -0
- package/framework/js/managers/interaction-manager.js +254 -0
- package/framework/js/managers/interaction-registry.js +96 -0
- package/framework/js/managers/objective-manager.js +423 -0
- package/framework/js/managers/score-manager.js +441 -0
- package/framework/js/managers/video-manager.js +536 -0
- package/framework/js/navigation/Breadcrumbs.js +234 -0
- package/framework/js/navigation/NavigationActions.js +1132 -0
- package/framework/js/navigation/NavigationState.js +276 -0
- package/framework/js/navigation/NavigationUI.js +574 -0
- package/framework/js/navigation/document-gallery.js +357 -0
- package/framework/js/navigation/navigation-helpers.js +175 -0
- package/framework/js/navigation/navigation-validators.js +174 -0
- package/framework/js/state/index.js +8 -0
- package/framework/js/state/lms-connection.js +482 -0
- package/framework/js/state/lms-error-utils.js +58 -0
- package/framework/js/state/state-commits.js +200 -0
- package/framework/js/state/state-domains.js +86 -0
- package/framework/js/state/state-manager.js +502 -0
- package/framework/js/state/state-validation.js +311 -0
- package/framework/js/state/transaction-log.js +41 -0
- package/framework/js/state/xapi-statement-service.js +325 -0
- package/framework/js/utilities/access-control.js +99 -0
- package/framework/js/utilities/breakpoint-manager.js +315 -0
- package/framework/js/utilities/canvas-slide.js +35 -0
- package/framework/js/utilities/conditional-display.js +388 -0
- package/framework/js/utilities/course-channel.js +214 -0
- package/framework/js/utilities/course-helpers.js +420 -0
- package/framework/js/utilities/data-reporter.js +273 -0
- package/framework/js/utilities/error-reporter.js +313 -0
- package/framework/js/utilities/hotspot-helper.js +341 -0
- package/framework/js/utilities/icons.js +348 -0
- package/framework/js/utilities/logger.js +92 -0
- package/framework/js/utilities/markdown-renderer.js +45 -0
- package/framework/js/utilities/scroll-tracker.js +68 -0
- package/framework/js/utilities/ui-initializer.js +146 -0
- package/framework/js/utilities/utilities.js +293 -0
- package/framework/js/utilities/view-manager.js +227 -0
- package/framework/js/validation/html-validators.js +422 -0
- package/framework/js/validation/scorm-validators.js +438 -0
- package/framework/js/vendor/pipwerks.js +931 -0
- package/framework/scripts/generate-narration.js +629 -0
- package/framework/scripts/tts-providers/azure-provider.js +178 -0
- package/framework/scripts/tts-providers/base-provider.js +81 -0
- package/framework/scripts/tts-providers/deepgram-provider.js +135 -0
- package/framework/scripts/tts-providers/elevenlabs-provider.js +148 -0
- package/framework/scripts/tts-providers/google-provider.js +272 -0
- package/framework/scripts/tts-providers/index.js +158 -0
- package/framework/scripts/tts-providers/openai-provider.js +143 -0
- package/framework/version.json +63 -0
- package/lib/authoring-api.js +919 -0
- package/lib/build-linter.js +450 -0
- package/lib/build-packaging.js +186 -0
- package/lib/build.js +88 -0
- package/lib/cloud.js +691 -0
- package/lib/convert.js +341 -0
- package/lib/course-parser.js +936 -0
- package/lib/course-writer.js +258 -0
- package/lib/create.js +248 -0
- package/lib/css-index.js +237 -0
- package/lib/dev.js +51 -0
- package/lib/export-content.js +1246 -0
- package/lib/headless-browser.js +413 -0
- package/lib/import.js +377 -0
- package/lib/index.js +80 -0
- package/lib/info.js +79 -0
- package/lib/interaction-formatters.js +568 -0
- package/lib/manifest/cmi5-manifest.js +63 -0
- package/lib/manifest/lti-tool-config.js +53 -0
- package/lib/manifest/manifest-factory.js +99 -0
- package/lib/manifest/scorm-12-manifest.js +61 -0
- package/lib/manifest/scorm-2004-manifest.js +94 -0
- package/lib/manifest/scorm-proxy-manifest.js +104 -0
- package/lib/manifest-parser.js +96 -0
- package/lib/mcp-prompts.js +753 -0
- package/lib/mcp-server.js +316 -0
- package/lib/narration.js +53 -0
- package/lib/pdf-structure.js +142 -0
- package/lib/preview-export.js +231 -0
- package/lib/preview-routes-api.js +662 -0
- package/lib/preview-routes-editing.js +159 -0
- package/lib/preview-routes-lms.js +230 -0
- package/lib/preview-server.js +564 -0
- package/lib/project-utils.js +269 -0
- package/lib/proxy-templates/proxy.html +68 -0
- package/lib/proxy-templates/scorm-bridge.js +112 -0
- package/lib/scaffold.js +193 -0
- package/lib/schema-extractor.js +361 -0
- package/lib/slide-source-editor.js +586 -0
- package/lib/stub-player/app-viewer.js +195 -0
- package/lib/stub-player/app.js +370 -0
- package/lib/stub-player/catalog-panel.js +312 -0
- package/lib/stub-player/config-panel.js +1303 -0
- package/lib/stub-player/content-generator.js +586 -0
- package/lib/stub-player/content-viewer.js +173 -0
- package/lib/stub-player/debug-panel.js +420 -0
- package/lib/stub-player/edit-mode.js +922 -0
- package/lib/stub-player/edit-utils.js +400 -0
- package/lib/stub-player/header-bar.js +354 -0
- package/lib/stub-player/interaction-editor.js +210 -0
- package/lib/stub-player/interactions-panel.js +565 -0
- package/lib/stub-player/lms-api.js +1094 -0
- package/lib/stub-player/login-screen.js +74 -0
- package/lib/stub-player/outline-mode.js +689 -0
- package/lib/stub-player/styles/_assessments-panel.css +245 -0
- package/lib/stub-player/styles/_base.css +89 -0
- package/lib/stub-player/styles/_catalog-icons.css +96 -0
- package/lib/stub-player/styles/_catalog-panel.css +291 -0
- package/lib/stub-player/styles/_config-panel.css +636 -0
- package/lib/stub-player/styles/_content-viewer.css +834 -0
- package/lib/stub-player/styles/_debug-panel.css +576 -0
- package/lib/stub-player/styles/_edit-mode.css +128 -0
- package/lib/stub-player/styles/_header-bar.css +343 -0
- package/lib/stub-player/styles/_interaction-editor.css +140 -0
- package/lib/stub-player/styles/_interactions-panel.css +1038 -0
- package/lib/stub-player/styles/_login-screen.css +102 -0
- package/lib/stub-player/styles/_outline-mode.css +752 -0
- package/lib/stub-player/styles.css +15 -0
- package/lib/stub-player.js +160 -0
- package/lib/test-data-reporting.js +176 -0
- package/lib/test-error-reporting.js +146 -0
- package/lib/token.js +86 -0
- package/lib/upgrade.js +257 -0
- package/lib/validation-rules.js +517 -0
- package/lib/vite-plugin-content-discovery.js +296 -0
- package/package.json +108 -0
- package/schemas/XMLSchema.dtd +402 -0
- package/schemas/adlcp_v1p3.xsd +111 -0
- package/schemas/adlnav_v1p3.xsd +61 -0
- package/schemas/adlseq_v1p3.xsd +93 -0
- package/schemas/common/anyElement.xsd +27 -0
- package/schemas/common/dataTypes.xsd +138 -0
- package/schemas/common/elementNames.xsd +767 -0
- package/schemas/common/elementTypes.xsd +786 -0
- package/schemas/common/rootElement.xsd +31 -0
- package/schemas/common/vocabTypes.xsd +345 -0
- package/schemas/common/vocabValues.xsd +257 -0
- package/schemas/datatypes.dtd +203 -0
- package/schemas/ims_xml.xsd +35 -0
- package/schemas/imscp_v1p1.xsd +368 -0
- package/schemas/imsss_v1p0.xsd +67 -0
- package/schemas/imsss_v1p0auxresource.xsd +19 -0
- package/schemas/imsss_v1p0control.xsd +20 -0
- package/schemas/imsss_v1p0delivery.xsd +17 -0
- package/schemas/imsss_v1p0limit.xsd +47 -0
- package/schemas/imsss_v1p0objective.xsd +67 -0
- package/schemas/imsss_v1p0random.xsd +16 -0
- package/schemas/imsss_v1p0rollup.xsd +46 -0
- package/schemas/imsss_v1p0seqrule.xsd +108 -0
- package/schemas/imsss_v1p0util.xsd +94 -0
- package/schemas/license.txt +17 -0
- package/schemas/lom.xsd +102 -0
- package/schemas/lomCustom.xsd +62 -0
- package/schemas/lomLoose.xsd +62 -0
- package/schemas/lomStrict.xsd +62 -0
- package/schemas/xml.xsd +81 -0
- package/template/.env.example +92 -0
- package/template/course/assets/audio/example-intro.mp3 +0 -0
- package/template/course/assets/audio/example-ui-demo--compact-player.mp3 +0 -0
- package/template/course/assets/audio/example-ui-demo--demo-modal.mp3 +0 -0
- package/template/course/assets/audio/example-ui-demo--full-player.mp3 +0 -0
- package/template/course/assets/docs/example_md_1.md +39 -0
- package/template/course/assets/docs/example_md_2.md +41 -0
- package/template/course/assets/docs/example_pdf_1_thumbnail.png +0 -0
- package/template/course/assets/docs/example_pdf_2.pdf +0 -0
- package/template/course/assets/images/course-architecture.svg +36 -0
- package/template/course/assets/images/logo.svg +14 -0
- package/template/course/assets/widgets/counter-demo.html +190 -0
- package/template/course/assets/widgets/gravity-painter.html +384 -0
- package/template/course/course-config.js +539 -0
- package/template/course/icons.js +19 -0
- package/template/course/interactions/PLUGIN_GUIDE.md +97 -0
- package/template/course/slides/example-course-structure.js +138 -0
- package/template/course/slides/example-final-exam.js +144 -0
- package/template/course/slides/example-finishing.js +127 -0
- package/template/course/slides/example-interactions-showcase.js +615 -0
- package/template/course/slides/example-preview-tour.js +129 -0
- package/template/course/slides/example-remedial.js +143 -0
- package/template/course/slides/example-summary.js +103 -0
- package/template/course/slides/example-ui-showcase.js +1805 -0
- package/template/course/slides/example-welcome.js +123 -0
- package/template/course/slides/example-workflow.js +140 -0
- package/template/course/theme.css +165 -0
- package/template/eslint.config.js +47 -0
- package/template/package.json +28 -0
- package/template/vite.config.js +339 -0
package/lib/css-index.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS Class Index
|
|
3
|
+
*
|
|
4
|
+
* Extracts all valid CSS class names and data-component values from
|
|
5
|
+
* framework and course CSS using PostCSS.
|
|
6
|
+
*
|
|
7
|
+
* Extracted from authoring-api.js to break a circular dependency:
|
|
8
|
+
* build-linter.js → authoring-api.js → (has spawn/exec/etc)
|
|
9
|
+
*
|
|
10
|
+
* Now both build-linter.js and authoring-api.js can import this
|
|
11
|
+
* without pulling in each other's dependency trees.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import fs from 'fs';
|
|
15
|
+
import path from 'path';
|
|
16
|
+
import { fileURLToPath } from 'url';
|
|
17
|
+
import postcss from 'postcss';
|
|
18
|
+
|
|
19
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
const __packageRoot = path.dirname(__dirname);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Find course root directory (cwd-first, then package root fallback).
|
|
24
|
+
*/
|
|
25
|
+
function getCourseRoot() {
|
|
26
|
+
if (fs.existsSync(path.join(process.cwd(), 'course'))) return process.cwd();
|
|
27
|
+
if (fs.existsSync(path.join(process.cwd(), 'template', 'course'))) return path.join(process.cwd(), 'template');
|
|
28
|
+
if (fs.existsSync(path.join(__packageRoot, 'course'))) return __packageRoot;
|
|
29
|
+
if (fs.existsSync(path.join(__packageRoot, 'template', 'course'))) return path.join(__packageRoot, 'template');
|
|
30
|
+
throw new Error('No course directory found.');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Find framework root directory.
|
|
35
|
+
*/
|
|
36
|
+
function getFrameworkRoot() {
|
|
37
|
+
const cwd = process.cwd();
|
|
38
|
+
if (fs.existsSync(path.join(cwd, 'framework'))) return cwd;
|
|
39
|
+
const parent = path.dirname(cwd);
|
|
40
|
+
if (fs.existsSync(path.join(parent, 'framework'))) return parent;
|
|
41
|
+
if (fs.existsSync(path.join(__packageRoot, 'framework'))) return __packageRoot;
|
|
42
|
+
throw new Error('Framework directory not found.');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Recursively collect .css files from a directory.
|
|
47
|
+
*/
|
|
48
|
+
export function collectCssFiles(dir, result) {
|
|
49
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
50
|
+
const fullPath = path.join(dir, entry.name);
|
|
51
|
+
if (entry.isDirectory()) {
|
|
52
|
+
collectCssFiles(fullPath, result);
|
|
53
|
+
} else if (entry.name.endsWith('.css')) {
|
|
54
|
+
result.push(fullPath);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Extract class names and data-component values from PostCSS nodes (recursive).
|
|
61
|
+
*/
|
|
62
|
+
function extractSelectorsFromNode(node, classes, dataComponents) {
|
|
63
|
+
if (node.type === 'rule' && node.selector) {
|
|
64
|
+
const classMatches = node.selector.match(/\.[\w-]+/g);
|
|
65
|
+
if (classMatches) {
|
|
66
|
+
for (const match of classMatches) {
|
|
67
|
+
classes.add(match.slice(1));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const dcMatches = node.selector.match(/\[data-component="([^"]+)"\]/g);
|
|
72
|
+
if (dcMatches) {
|
|
73
|
+
for (const match of dcMatches) {
|
|
74
|
+
const value = match.match(/\[data-component="([^"]+)"\]/);
|
|
75
|
+
if (value) dataComponents.add(value[1]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (node.nodes) {
|
|
81
|
+
for (const child of node.nodes) {
|
|
82
|
+
extractSelectorsFromNode(child, classes, dataComponents);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Extract all valid CSS class names and data-component values from framework and course CSS.
|
|
89
|
+
* Uses PostCSS for proper parsing — handles @media, nesting, pseudo-classes correctly.
|
|
90
|
+
*/
|
|
91
|
+
export function getValidCssClasses() {
|
|
92
|
+
const frameworkRoot = getFrameworkRoot();
|
|
93
|
+
const cssDir = path.join(frameworkRoot, 'framework', 'css');
|
|
94
|
+
|
|
95
|
+
const classes = new Set();
|
|
96
|
+
const dataComponents = new Set();
|
|
97
|
+
const cssFiles = [];
|
|
98
|
+
|
|
99
|
+
// 1. Framework CSS (recursive)
|
|
100
|
+
if (fs.existsSync(cssDir)) {
|
|
101
|
+
collectCssFiles(cssDir, cssFiles);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 2. Course CSS (theme + custom components)
|
|
105
|
+
try {
|
|
106
|
+
const courseRoot = getCourseRoot();
|
|
107
|
+
const courseDir = path.join(courseRoot, 'course');
|
|
108
|
+
const themeFile = path.join(courseDir, 'theme.css');
|
|
109
|
+
if (fs.existsSync(themeFile)) cssFiles.push(themeFile);
|
|
110
|
+
|
|
111
|
+
const customComponentsDir = path.join(courseDir, 'components');
|
|
112
|
+
if (fs.existsSync(customComponentsDir)) {
|
|
113
|
+
collectCssFiles(customComponentsDir, cssFiles);
|
|
114
|
+
}
|
|
115
|
+
} catch {
|
|
116
|
+
// No course directory — framework-only mode
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Parse each CSS file with PostCSS
|
|
120
|
+
for (const file of cssFiles) {
|
|
121
|
+
try {
|
|
122
|
+
const source = fs.readFileSync(file, 'utf-8');
|
|
123
|
+
const root = postcss.parse(source, { from: file });
|
|
124
|
+
extractSelectorsFromNode(root, classes, dataComponents);
|
|
125
|
+
} catch {
|
|
126
|
+
// Skip unparseable CSS files
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const sortedClasses = [...classes].sort();
|
|
131
|
+
const sortedComponents = [...dataComponents].sort();
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
classes: sortedClasses,
|
|
135
|
+
dataComponents: sortedComponents,
|
|
136
|
+
classCount: sortedClasses.length,
|
|
137
|
+
componentCount: sortedComponents.length,
|
|
138
|
+
cssFiles: cssFiles.map(f => path.relative(frameworkRoot, f)),
|
|
139
|
+
message: `${sortedClasses.length} CSS classes and ${sortedComponents.length} data-component values found across ${cssFiles.length} CSS files.`
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Files that legitimately use global element selectors (resets, typography, tokens)
|
|
144
|
+
const EXEMPT_FILES = new Set([
|
|
145
|
+
'01-base.css',
|
|
146
|
+
'design-tokens.css',
|
|
147
|
+
'accessibility.css',
|
|
148
|
+
'accessibility-utils.css',
|
|
149
|
+
'framework.css',
|
|
150
|
+
'forms.css',
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Check if a selector is a bare element selector (no class/ID/data-attribute qualifier).
|
|
155
|
+
* Returns the offending element name, or null if the selector is qualified.
|
|
156
|
+
*
|
|
157
|
+
* Examples:
|
|
158
|
+
* "button" → "button"
|
|
159
|
+
* "button[type=\"button\"]" → "button[type=\"button\"]"
|
|
160
|
+
* "button:disabled" → "button:disabled"
|
|
161
|
+
* ".btn" → null (class-qualified)
|
|
162
|
+
* "button.sidebar-item" → null (class-qualified)
|
|
163
|
+
* "[data-theme] button" → null (scoped by data-attribute)
|
|
164
|
+
* "#prevBtn" → null (ID-qualified)
|
|
165
|
+
*/
|
|
166
|
+
function isBareElementSelector(selector) {
|
|
167
|
+
// Skip selectors that contain a class, ID, or data-attribute qualifier
|
|
168
|
+
if (/[.#]/.test(selector) || /\[data-/.test(selector)) return null;
|
|
169
|
+
|
|
170
|
+
// Check if the selector starts with or is a bare element name
|
|
171
|
+
// Match: element, element:pseudo, element[attr], element > element
|
|
172
|
+
const elementMatch = selector.trim().match(/^([a-z][a-z0-9-]*)/i);
|
|
173
|
+
if (elementMatch) {
|
|
174
|
+
return selector.trim();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Lint CSS selectors for bare element selectors in framework CSS.
|
|
182
|
+
* Catches rules like `button { }`, `button[type="button"] { }`, `button:disabled { }`
|
|
183
|
+
* that apply styles globally to all matching elements without class qualification.
|
|
184
|
+
*
|
|
185
|
+
* @returns {{ warnings: string[] }} Lint warnings
|
|
186
|
+
*/
|
|
187
|
+
export function lintCssSelectors() {
|
|
188
|
+
const frameworkRoot = getFrameworkRoot();
|
|
189
|
+
const cssDir = path.join(frameworkRoot, 'framework', 'css');
|
|
190
|
+
const warnings = [];
|
|
191
|
+
|
|
192
|
+
if (!fs.existsSync(cssDir)) return { warnings };
|
|
193
|
+
|
|
194
|
+
const cssFiles = [];
|
|
195
|
+
collectCssFiles(cssDir, cssFiles);
|
|
196
|
+
|
|
197
|
+
for (const file of cssFiles) {
|
|
198
|
+
const basename = path.basename(file);
|
|
199
|
+
if (EXEMPT_FILES.has(basename)) continue;
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
const source = fs.readFileSync(file, 'utf-8');
|
|
203
|
+
const root = postcss.parse(source, { from: file });
|
|
204
|
+
const relPath = path.relative(path.join(frameworkRoot, 'framework', 'css'), file);
|
|
205
|
+
|
|
206
|
+
root.walk(node => {
|
|
207
|
+
if (node.type !== 'rule' || !node.selector) return;
|
|
208
|
+
|
|
209
|
+
// Skip rules inside @keyframes (from/to are keywords, not selectors)
|
|
210
|
+
let parent = node.parent;
|
|
211
|
+
while (parent) {
|
|
212
|
+
if (parent.type === 'atrule' && parent.name.endsWith('keyframes')) return;
|
|
213
|
+
parent = parent.parent;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Split comma-separated selectors and check each
|
|
217
|
+
const selectors = node.selector.split(',').map(s => s.trim());
|
|
218
|
+
for (const sel of selectors) {
|
|
219
|
+
const bare = isBareElementSelector(sel);
|
|
220
|
+
if (bare) {
|
|
221
|
+
const line = node.source?.start?.line || '?';
|
|
222
|
+
warnings.push(
|
|
223
|
+
`CSS global selector: '${bare}' in ${relPath}:${line}. ` +
|
|
224
|
+
'Bare element selectors apply styles to ALL matching elements. ' +
|
|
225
|
+
'Use a class qualifier (e.g. .btn) instead.'
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
} catch {
|
|
231
|
+
// Skip unparseable files
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return { warnings };
|
|
236
|
+
}
|
|
237
|
+
|
package/lib/dev.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev command - start Vite development server
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { spawn } from 'child_process';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import { validateProject } from './project-utils.js';
|
|
9
|
+
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
12
|
+
export async function dev(options = {}) {
|
|
13
|
+
validateProject({ warnMissingRc: true });
|
|
14
|
+
|
|
15
|
+
console.log(`
|
|
16
|
+
🚀 Starting CourseCode development build...
|
|
17
|
+
|
|
18
|
+
Building in watch mode - changes will auto-rebuild
|
|
19
|
+
Output: dist/
|
|
20
|
+
Press Ctrl+C to stop
|
|
21
|
+
`);
|
|
22
|
+
|
|
23
|
+
// Run vite build in watch mode
|
|
24
|
+
// Pass LMS_FORMAT if specified via --format flag
|
|
25
|
+
const env = { ...process.env };
|
|
26
|
+
// Signal to framework reporters that this is a local dev build
|
|
27
|
+
env.VITE_COURSECODE_LOCAL = 'true';
|
|
28
|
+
if (options.format) {
|
|
29
|
+
env.LMS_FORMAT = options.format;
|
|
30
|
+
console.log(` 📦 Format: ${options.format}\n`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const viteBuild = spawn('npx', ['vite', 'build', '--mode', 'development', '--watch'], {
|
|
34
|
+
cwd: process.cwd(),
|
|
35
|
+
stdio: 'inherit',
|
|
36
|
+
shell: true,
|
|
37
|
+
env
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
viteBuild.on('error', (error) => {
|
|
41
|
+
console.error('❌ Failed to start dev server:', error.message);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Handle Ctrl+C gracefully
|
|
46
|
+
process.on('SIGINT', () => {
|
|
47
|
+
console.log('\n\n👋 Stopping development server...\n');
|
|
48
|
+
viteBuild.kill();
|
|
49
|
+
process.exit(0);
|
|
50
|
+
});
|
|
51
|
+
}
|