coursecast 0.3.1 → 0.3.2
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/package.json
CHANGED
|
@@ -141,10 +141,41 @@ export function validateLesson(obj) {
|
|
|
141
141
|
if (obj.simulation) {
|
|
142
142
|
if (!obj.simulation.html) e.push('"simulation" is missing "html"')
|
|
143
143
|
if (!obj.simulation.js) e.push('"simulation" is missing "js"')
|
|
144
|
+
e.push(...validateSimulationWiring(obj.simulation))
|
|
144
145
|
}
|
|
145
146
|
return e
|
|
146
147
|
}
|
|
147
148
|
|
|
149
|
+
// The simulation's markup and its script are written as two separate strings, so a typo in
|
|
150
|
+
// one doesn't show up until a learner clicks and the console throws. These checks catch the
|
|
151
|
+
// two ways that actually happens, and because they run inside validate+repair the model gets
|
|
152
|
+
// a chance to fix its own mistake before the lesson is ever rendered.
|
|
153
|
+
function validateSimulationWiring({ html = '', js = '' }) {
|
|
154
|
+
const e = []
|
|
155
|
+
if (!html || !js) return e
|
|
156
|
+
|
|
157
|
+
// 1. inline handlers (onclick="doThing()") that name a function the script never defines
|
|
158
|
+
const handlers = new Set()
|
|
159
|
+
for (const m of String(html).matchAll(/\son[a-z]+\s*=\s*(["'])(.*?)\1/gi)) {
|
|
160
|
+
const fn = m[2].match(/^\s*([A-Za-z_$][\w$]*)\s*\(/)
|
|
161
|
+
if (fn) handlers.add(fn[1])
|
|
162
|
+
}
|
|
163
|
+
for (const fn of handlers) {
|
|
164
|
+
const defined = new RegExp(
|
|
165
|
+
`(function\\s+${fn}\\b|\\b(?:var|let|const)\\s+${fn}\\s*=|\\b${fn}\\s*=\\s*(?:function|\\()|window\\.${fn}\\s*=)`,
|
|
166
|
+
).test(js)
|
|
167
|
+
if (!defined) e.push(`simulation.html calls "${fn}()" from an inline handler, but simulation.js never defines it`)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// 2. getElementById('x') where the markup has no element with that id
|
|
171
|
+
const ids = new Set([...String(html).matchAll(/\sid\s*=\s*(["'])(.*?)\1/gi)].map((m) => m[2].trim()))
|
|
172
|
+
for (const m of String(js).matchAll(/getElementById\(\s*(["'])(.*?)\1\s*\)/gi)) {
|
|
173
|
+
const want = m[2].trim()
|
|
174
|
+
if (want && !ids.has(want)) e.push(`simulation.js looks up id "${want}", which does not exist in simulation.html`)
|
|
175
|
+
}
|
|
176
|
+
return e.slice(0, 6)
|
|
177
|
+
}
|
|
178
|
+
|
|
148
179
|
export function validateSyllabus(obj) {
|
|
149
180
|
const e = []
|
|
150
181
|
if (!obj || typeof obj !== 'object') return ['the output was not a JSON object']
|