-
-
Notifications
You must be signed in to change notification settings - Fork 202
Local modules! #858
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Local modules! #858
Conversation
β Deploy Preview for livecodes ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Size Change: +3.56 kB (+0.37%) Total Size: 960 kB
βΉοΈ View Unchanged
|
Deploying livecodes with Β
|
| Latest commit: |
e11fc0d
|
| Status: | Β β Β Deploy successful! |
| Preview URL: | https://b6ede0dc.livecodes.pages.dev |
| Branch Preview URL: | https://local-modules.livecodes.pages.dev |
WalkthroughAdds an experimental Local Modules mode (build-time downloader and runtime local /modules/ resolution), broad vendor URL refactors, Go (Wasm) language + starter and docs/i18n, async I/O server refactors, Docker/build arg additions, various client imports switched to dynamic module-service URLs, and ancillary lint/docs/test updates. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Builder as Docker build
participant Downloader as download-modules.js
participant Image as Build image (.cache/modules)
participant Server as App server (runtime)
participant Browser as Browser
Builder->>Downloader: If LOCAL_MODULES=true -> run download-modules
Downloader-->>Image: Populate .cache/modules (fonts, libs, pyodide, files)
Image->>Server: Include /modules assets in image
Browser->>Server: Request app + /modules/...
Server->>Browser: Serve /modules/* with immutable cache headers
Browser->>Browser: modulesService.getUrl(...) resolves to /modules/... -> app loads resources offline
Estimated code review effortπ― 4 (Complex) | β±οΈ ~60 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touchesβ Failed checks (2 warnings)
β Passed checks (3 passed)
β¨ Finishing touches
π§ͺ Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
π§Ή Nitpick comments (2)
docs/docs/languages/python-wasm.mdx (1)
104-104: Doc version bump looks correctPyodide 0.28.0 ships Python 3.13.2, so the wording is accurate. Ensure the live demo URLs (
pyodideBaseUrlvalues) were updated everywhere else; mismatched versions silently break runtime loading.docs/docs/advanced/docker.mdx (1)
184-186: Consider the suggested style improvementsThe static analysis tool suggests some minor improvements for clarity:
- Replace "a large number of" with "many" or "numerous" for conciseness
- Add periods after "etc" in American English style
- Consider replacing "in spite of" with "despite" for brevity
-LiveCodes depends on a large number of external modules to support a wide range of features (e.g. code editors, compilers, formatters, etc). -These modules are loaded from CDNs (e.g. [jsDelivr](https://www.jsdelivr.com/), [unpkg](https://unpkg.com/), etc). -So, in spite of being a client-side app, LiveCodes requires an internet connection to load these modules. +LiveCodes depends on numerous external modules to support a wide range of features (e.g. code editors, compilers, formatters, etc.). +These modules are loaded from CDNs (e.g. [jsDelivr](https://www.jsdelivr.com/), [unpkg](https://unpkg.com/), etc.). +So, despite being a client-side app, LiveCodes requires an internet connection to load these modules.
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
β Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
π Files selected for processing (30)
.nvmrc(1 hunks)Dockerfile(2 hunks)docker-compose.yml(2 hunks)docs/docs/advanced/docker.mdx(2 hunks)docs/docs/languages/python-wasm.mdx(1 hunks)eslint.config.mjs(1 hunks)package.json(3 hunks)scripts/build.js(3 hunks)scripts/download-modules.js(1 hunks)scripts/utils.js(4 hunks)server/src/app.ts(1 hunks)src/404.html(1 hunks)src/_headers(1 hunks)src/livecodes/compiler/compile.worker.ts(2 hunks)src/livecodes/editor/codemirror/codemirror.ts(1 hunks)src/livecodes/editor/codemirror/editor-languages.ts(1 hunks)src/livecodes/editor/fonts.ts(11 hunks)src/livecodes/editor/monaco/monaco.ts(2 hunks)src/livecodes/html/app.html(1 hunks)src/livecodes/languages/clojurescript/lang-clojurescript.ts(1 hunks)src/livecodes/languages/diagrams/lang-diagrams-compiler-esm.ts(3 hunks)src/livecodes/languages/postgresql/lang-postgresql-compiler-esm.ts(2 hunks)src/livecodes/languages/python-wasm/lang-python-wasm-script.ts(1 hunks)src/livecodes/languages/rescript/lang-rescript-compiler-esm.ts(2 hunks)src/livecodes/languages/rescript/lang-rescript-formatter.ts(1 hunks)src/livecodes/languages/utils.ts(2 hunks)src/livecodes/main.ts(3 hunks)src/livecodes/services/modules.ts(4 hunks)src/livecodes/types/bundle-types.ts(2 hunks)src/livecodes/vendors.ts(12 hunks)
π§° Additional context used
𧬠Code Graph Analysis (14)
src/livecodes/languages/rescript/lang-rescript-formatter.ts (3)
src/sdk/models.ts (1)
LanguageFormatter(1265-1267)src/livecodes/utils/utils.ts (1)
getAbsoluteUrl(83-84)src/livecodes/vendors.ts (1)
rescriptCdnUrl1(388-390)
src/livecodes/languages/clojurescript/lang-clojurescript.ts (1)
src/livecodes/vendors.ts (1)
cherryCljsBaseUrl(54-54)
src/livecodes/editor/codemirror/codemirror.ts (2)
src/livecodes/services/modules.ts (2)
modulesService(35-87)getAppCDN(89-97)src/livecodes/vendors.ts (1)
codeMirrorBasePath(96-96)
src/livecodes/languages/postgresql/lang-postgresql-compiler-esm.ts (1)
src/livecodes/vendors.ts (1)
pgliteBaseUrl(328-328)
src/livecodes/languages/rescript/lang-rescript-compiler-esm.ts (1)
src/livecodes/vendors.ts (4)
rescriptCdnUrl1(388-390)rescriptCdnUrl2(391-393)rescriptCdnUrl3(394-396)rescriptCdnUrl4(397-399)
src/livecodes/editor/codemirror/editor-languages.ts (2)
src/livecodes/services/modules.ts (2)
modulesService(35-87)getAppCDN(89-97)src/livecodes/vendors.ts (1)
codeMirrorBasePath(96-96)
src/livecodes/languages/utils.ts (1)
src/livecodes/vendors.ts (1)
vendorsBaseUrl(13-14)
src/livecodes/main.ts (2)
src/livecodes/services/modules.ts (1)
modulesService(35-87)src/livecodes/vendors.ts (1)
codeMirrorBasePath(96-96)
src/livecodes/compiler/compile.worker.ts (2)
src/livecodes/services/modules.ts (2)
modulesService(35-87)getAppCDN(89-97)src/livecodes/vendors.ts (1)
codeMirrorBasePath(96-96)
src/livecodes/editor/fonts.ts (1)
src/livecodes/vendors.ts (19)
fontAnonymousProBaseUrl(138-138)fontAstigmataBaseUrl(140-142)fontCascadiaCodeBaseUrl(146-146)fontComicMonoBaseUrl(152-152)fontCourierPrimeBaseUrl(154-154)fontDejaVuMonoBaseUrl(160-160)fontFantasqueBaseUrl(162-164)fontFiraCodeBaseUrl(166-166)fontHackBaseUrl(172-172)fontHermitBaseUrl(174-174)fontIosevkaBaseUrl(188-188)fontJetbrainsMonoBaseUrl(190-192)fontMonoidBaseUrl(208-208)fontOpenDyslexicBaseUrl(218-218)fontRobotoMonoBaseUrl(224-224)fontSourceCodeProBaseUrl(230-232)fontSpaceMonoBaseUrl(234-234)fontUbuntuMonoBaseUrl(240-240)fontVictorMonoBaseUrl(242-242)
src/livecodes/languages/diagrams/lang-diagrams-compiler-esm.ts (2)
src/livecodes/utils/utils.ts (1)
loadScript(175-216)src/livecodes/vendors.ts (2)
mermaidCdnUrl(298-298)hpccJsCdnBaseUrl(258-258)
src/livecodes/editor/monaco/monaco.ts (1)
src/livecodes/vendors.ts (1)
monacoVolarBaseUrl(312-312)
src/livecodes/types/bundle-types.ts (1)
src/livecodes/vendors.ts (1)
vendorsBaseUrl(13-14)
src/livecodes/vendors.ts (1)
src/livecodes/services/modules.ts (1)
modulesService(35-87)
πͺ LanguageTool
docs/docs/advanced/docker.mdx
[style] ~184-~184: To reduce wordiness, try specifying a number or using βmanyβ or βnumerousβ instead.
Context: ...s (EXPERIMENTAL) LiveCodes depends on a large number of external modules to support a wide rang...
(LARGE_NUMBER_OF)
[style] ~184-~184: In American English, abbreviations like βetc.β require a period.
Context: ...g. code editors, compilers, formatters, etc). These modules are loaded from CDNs (e...
(ETC_PERIOD)
[style] ~185-~185: In American English, abbreviations like βetc.β require a period.
Context: ...ivr.com/), unpkg, etc). So, in spite of being a client-side a...
(ETC_PERIOD)
[style] ~186-~186: βin spite ofβ might be wordy. Consider a shorter alternative.
Context: ... unpkg, etc). So, in spite of being a client-side app, LiveCodes requ...
(EN_WORDINESS_PREMIUM_IN_SPITE_OF)
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
- GitHub Check: Redirect rules - livecodes
- GitHub Check: Header rules - livecodes
- GitHub Check: Pages changed - livecodes
- GitHub Check: type-check (18.x)
- GitHub Check: build (18.x)
- GitHub Check: build
- GitHub Check: tests (18.x, 2)
- GitHub Check: tests (18.x, 4)
- GitHub Check: tests (18.x, 3)
- GitHub Check: tests (18.x, 1)
- GitHub Check: tests (18.x, 5)
- GitHub Check: type-check (18.x)
- GitHub Check: build (18.x)
π Additional comments (53)
.nvmrc (1)
1-1: Ensure Node 24 Readiness Across Your ToolchainIt looks like none of your package.json files declare an
engines.nodefield, so thereβs no automatic guard against running on the wrong Node version. Before merging this bump to v24.4.1, please double-check the following:β’ Add or update
engines.nodein each package.json (root, docs, server, storybook) to>=24so npm/yarn will warn on unsupported environments.
β’ Verify your CI/production images (Dockerfiles, GitHub Actions workflows, etc.) have been updated to pull Node 24.x.
β’ Confirm all major dependencies (including any native-addon prebuilds, husky hooks, linters, test runners) officially support Node 24.src/404.html (1)
145-147: Link target changed β confirm no regressions
Docsnow points to/docsinstead of/docs/overview.
Make sure the doc siteβs default route handles this (otherwise users will land on a blank page in SPAs when JS is disabled).No code change needed if
/docsalready redirects/serves the TOC; otherwise consider keeping the old deep link.eslint.config.mjs (1)
34-35: π Good call adding.cacheto ESLint ignore listThis prevents ESLint from grinding through ~1.5 GB of vendored modules in offline mode.
No further action required.src/livecodes/languages/rescript/lang-rescript-formatter.ts (1)
3-10: RespectbaseUrlwhen resolving the compiler URL
rescriptCdnUrl1may already resolve to a local path, but if a caller purposely overridesbaseUrl(e.g. embedding LiveCodes in a sub-folder), the new logic ignores it.
Safer variant:- importScripts(getAbsoluteUrl(rescriptCdnUrl1, baseUrl)); + const url = getAbsoluteUrl(rescriptCdnUrl1, baseUrl); + importScripts(url);This keeps the current behaviour while still letting
baseUrlwin when the constant is relative.Likely an incorrect or invalid review comment.
server/src/app.ts (1)
43-43: LGTM! Cache control path updated correctly for local modules.The change from
/assets/to/modules/aligns with the local modules feature implementation and maintains appropriate long-term caching for immutable module resources.src/livecodes/languages/postgresql/lang-postgresql-compiler-esm.ts (2)
3-3: LGTM! Import updated to use base URL pattern.The change from
pgliteUrltopgliteBaseUrlaligns with the standardized approach for vendor URL management in the local modules feature.
14-14: LGTM! Dynamic import correctly uses base URL with explicit file path.The change to
pgliteBaseUrl + 'index.js'provides explicit control over the module path and supports both local and remote module resolution.src/livecodes/languages/clojurescript/lang-clojurescript.ts (1)
26-28: LGTM! Import paths standardized to use base URL pattern.The changes from local paths (
'lib/...') tocherryCljsBaseUrl + 'lib/...'ensure consistency with other cherry-cljs imports and support the local modules feature.src/livecodes/languages/python-wasm/lang-python-wasm-script.ts (1)
2-2: Ensure FontAwesome stylesheet is still loaded for matplotlib iconsThe removal of the
fontAwesomeUrlimport and its dynamic stylesheet injection in
src/livecodes/languages/python-wasm/lang-python-wasm-script.ts(prepareEnv) means we no longer see where the FontAwesome CSS is applied. Althoughsrc/livecodes/vendors.tsstill exportsfontAwesomeBaseUrl, I didnβt find any other injection of the stylesheet elsewhere in the TS/JS code.Please confirm (either via a UI smoke-test or by inspecting your global startup modules) that the FontAwesome stylesheet is still being loaded so matplotlib icons continue to render properly.
β’ File: src/livecodes/languages/python-wasm/lang-python-wasm-script.ts (prepareEnv)
β’ File: src/livecodes/vendors.ts (exporting fontAwesomeBaseUrl)src/_headers (2)
1-2: LGTM! Header configuration updated for new asset organization.The change from
/assets/*to/livecodes/*aligns with the broader reorganization of static asset serving paths.
4-5: LGTM! Cache control configured correctly for local modules.The
/modules/*path now receives immutable caching, which is appropriate for versioned modules and aligns with the server-side cache control changes inserver/src/app.ts.src/livecodes/html/app.html (1)
366-366: Good template abstraction for conditional polyfill loading.The replacement of the hardcoded polyfill script with the
{{polyfillScript}}template placeholder enables dynamic control over polyfill inclusion based on theLOCAL_MODULESenvironment variable. This supports the offline functionality effectively.src/livecodes/editor/monaco/monaco.ts (2)
27-27: Correct import alias update for URL standardization.The change from
monacoVolarUrltomonacoVolarBaseUrlaligns with the broader effort to standardize vendor URLs from full file URLs to base URLs.
268-268: Proper dynamic import path construction.The explicit concatenation of
'index.js'to the base URL correctly implements the new pattern where base URLs are used with explicit file suffixes appended at import sites.src/livecodes/editor/codemirror/codemirror.ts (3)
44-44: Correct imports for dynamic URL resolution.Adding
getAppCDNandmodulesServiceimports enables dynamic URL resolution based on configuration, supporting the local modules feature.
46-46: Proper import change for URL standardization.The change from
codeMirrorBaseUrltocodeMirrorBasePathaligns with the pattern of using base paths that are dynamically resolved to URLs.
51-51: Effective dynamic URL construction.The dynamic construction of
codeMirrorBaseUrlusingmodulesService.getUrl(codeMirrorBasePath, getAppCDN())properly enables local vs CDN module resolution based on configuration.src/livecodes/languages/utils.ts (2)
3-3: Correct import change for URL standardization.The change from
highlightjsUrltovendorsBaseUrlaligns with the broader effort to standardize vendor URLs from specific file URLs to base URLs.
100-101: Proper dynamic import path construction.The explicit concatenation of
'highlight.js/highlight.js'to thevendorsBaseUrlcorrectly implements the new pattern where base URLs are used with explicit file paths appended at import sites.src/livecodes/editor/codemirror/editor-languages.ts (3)
15-15: Correct imports for dynamic URL resolution.Adding
getAppCDNandmodulesServiceimports enables dynamic URL resolution based on configuration, supporting the local modules feature.
16-16: Proper import change for URL standardization.The change from
codeMirrorBaseUrltocodeMirrorBasePathaligns with the pattern of using base paths that are dynamically resolved to URLs.
21-21: Effective dynamic URL construction.The dynamic construction of
codeMirrorBaseUrlusingmodulesService.getUrl(codeMirrorBasePath, getAppCDN())properly enables local vs CDN module resolution for CodeMirror language modules.src/livecodes/languages/rescript/lang-rescript-compiler-esm.ts (2)
9-12: LGTM! URL import standardization looks good.The change from importing a single base URL to four explicit URL constants aligns with the broader standardization effort across the codebase. This improves clarity by making the exact resources being loaded more explicit.
63-63: LGTM! Explicit URL array improves clarity.The change from base URL concatenation to an explicit array of full URLs makes the required ReScript compiler modules more transparent and easier to understand. This follows the same pattern being applied across other language compilers in the codebase.
src/livecodes/types/bundle-types.ts (1)
3-3: LGTM! Base URL standardization implemented correctly.The change from using a specific URL constant to dynamically constructing the import path using
vendorsBaseUrlaligns with the broader URL standardization effort across the codebase. This approach provides better flexibility for the local modules feature while maintaining the same functionality.Also applies to: 65-65
package.json (3)
30-31: LGTM! New scripts support the local modules feature.The
copy:modulesanddownload-modulesscripts are well-integrated additions that support the new local modules functionality. These scripts align with the PR objectives to enable offline operation by downloading and serving external modules locally.
125-126: LGTM! Dependencies added for module archive extraction.The
decompressanddecompress-tarbz2devDependencies are appropriate additions for handling compressed module archives (like Pyodide releases) as part of the local modules download process.
164-166: LGTM! Formatting improvements enhance readability.The reformatting of the
prettierplugins andjestconfiguration arrays from single-line to multi-line format improves readability and makes future modifications easier to track in diffs.Also applies to: 171-189
src/livecodes/editor/fonts.ts (2)
2-33: LGTM! Font URL imports standardized correctly.The conversion from specific font URL constants to base URL constants follows the established pattern across the codebase. This standardization improves consistency and supports the local modules feature.
47-227: LGTM! Dynamic font URL construction implemented properly.The changes to construct font URLs by concatenating base URLs with appropriate CSS filenames is well-implemented. The different CSS filenames (e.g., 'index.css', 'fira_code.css', 'hack.css') correctly reflect the actual structure of each font package.
src/livecodes/compiler/compile.worker.ts (2)
5-5: LGTM! Module service integration for dynamic URL resolution.The addition of
getAppCDNandmodulesServiceimports, along with the change fromcodeMirrorBaseUrltocodeMirrorBasePath, properly sets up the dynamic URL resolution pattern needed for the local modules feature.Also applies to: 7-7
270-270: LGTM! Dynamic URL construction implemented correctly.The change from static URL concatenation to using
modulesService.getUrl(codeMirrorBasePath, getAppCDN())properly implements the dynamic URL resolution pattern. This enables the worker to load CodeMirror resources from either CDN or local modules depending on the configuration.src/livecodes/main.ts (3)
8-8: LGTM - Import change aligns with URL standardization.The import change from
codeMirrorBaseUrltocodeMirrorBasePathis consistent with the broader refactoring to use base paths with dynamic URL resolution.
80-80: LGTM - Dynamic URL resolution supports local modules.The change from static import to dynamic URL resolution using
modulesService.getUrl(codeMirrorBasePath, appCDN)properly supports the new local modules feature while maintaining backward compatibility.
109-119: LGTM - Conditional polyfill script supports offline mode.The conditional logic correctly omits the external polyfill script when
LOCAL_MODULESis'true', which is appropriate for offline operation. The string comparison is consistent with how the environment variable is handled elsewhere in the codebase.Dockerfile (3)
1-1: LGTM - Node.js and Alpine version updates.The updates from
node:24.1.0-alpine3.21tonode:24.4.1-alpine3.22bring security patches and improvements. These are reasonable version bumps.Also applies to: 42-42
15-16: LGTM - New build arguments support local modules feature.The addition of
HOST_NAME,PORT,LOCAL_MODULES, andNODE_OPTIONSbuild arguments properly supports the new local modules functionality and self-hosted deployments.Also applies to: 24-25
27-33: LGTM - Efficient conditional module download.The file copying strategy and conditional download step are well-designed:
- Essential files are copied first to enable the download script
- The conditional
npm run download-modulesonly runs whenLOCAL_MODULES="true"- This approach optimizes Docker layer caching
scripts/build.js (4)
39-39: LGTM - JSDoc type annotation improves code documentation.The addition of JSDoc type annotation
/** @type {string} */for the content parameter enhances code documentation and type safety.
53-59: LGTM - Well-implemented URL replacement function.The
useSelfHostedURLfunction correctly:
- Guards against missing
HOST_NAMEenvironment variable- Constructs HTTPS URLs with proper port handling (omits port 443)
- Uses
replaceAllfor complete URL replacement
76-76: LGTM - Proper application of URL replacement to index.html.The change from
addBaseUrltouseSelfHostedURLforindex.htmlcorrectly applies the self-hosted URL replacement functionality.
79-87: LGTM - Consistent URL replacement for web manifest.The site.webmanifest processing correctly applies the same URL replacement logic, ensuring consistency across all build artifacts.
scripts/utils.js (3)
75-75: LGTM - Consistent LOCAL_MODULES environment variable handling.The
localModulesvariable follows the same pattern as other boolean environment variables, usingString(process.env.LOCAL_MODULES) === 'true'for proper boolean conversion.Also applies to: 90-90
109-109: LGTM - Environment variable properly exposed to build process.The
LOCAL_MODULESenvironment variable is correctly destructured and included in the build-time environment variables map, making it available during the build process.Also applies to: 125-125
130-137: LGTM - Improved module export formatting.The reformatting from single-line to multi-line export object improves code readability without changing functionality.
src/livecodes/languages/diagrams/lang-diagrams-compiler-esm.ts (3)
19-19: LGTM - Import change aligns with URL standardization.The change from
hpccJsCdnUrltohpccJsCdnBaseUrlis consistent with the broader refactoring to use base paths invendors.ts.
183-183: LGTM - Improved Mermaid loading with loadScript utility.The change from direct
import(mermaidCdnUrl)toloadScript(mermaidCdnUrl, 'mermaid')provides better error handling and supports both browser and web worker environments, which is beneficial for the local modules feature.
204-204: LGTM - Explicit file path construction for Graphviz.The change from
import(hpccJsCdnUrl)toimport(hpccJsCdnBaseUrl + 'index.js')correctly constructs the full file path from the base URL, which is consistent with the new approach of using base paths and explicit file references.docs/docs/advanced/docker.mdx (1)
172-174: LGTM! Clear documentation for the local modules feature.The documentation effectively explains the experimental local modules feature with appropriate warnings and limitations.
src/livecodes/services/modules.ts (1)
69-73: LGTM! Well-implemented local CDN integration.The local modules URL is properly integrated into the CDN checking mechanism, placed strategically between the preferred CDN and the npm CDN list.
src/livecodes/vendors.ts (3)
3-10: Excellent documentation of URL handling conventions!The comments clearly explain the URL handling rules and exceptions, which will help maintain consistency in future updates.
388-399: LGTM! Clean separation of ReScript CDN URLs.The split from a single base URL to four individual URLs provides better granularity for the module download process.
141-142: Ensure the GitHub hash points to a stable releaseI wasnβt able to find any tag or release matching commit
6d0ee00a07fb1932902f0b81a504d075d47bd52fin hatemhosny/astigmata-font. Pinning to a raw SHA is stable long-term but carries risk if the repo history is ever rewritten. Please:
- Confirm this SHA belongs to an immutable, release-ready commit (e.g. part of a maintained release branch).
- If possible, switch to referencing an official tag (for example,
vX.Y.Z) instead of the bare SHA.Location:
β’ src/livecodes/vendors.ts, lines 141β142
| const transformVendorsModule = (/** @type {string} */ content) => | ||
| 'const modulesService = { getUrl: (mod) => mod };\n' + | ||
| content.replace('import', '// import').replace('process.env.SDK_VERSION', `"${sdkPkg.version}"`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve import statement replacement to avoid unintended replacements
The current string replacement might inadvertently replace the word "import" in other contexts (e.g., within strings or comments).
const transformVendorsModule = (/** @type {string} */ content) =>
'const modulesService = { getUrl: (mod) => mod };\n' +
- content.replace('import', '// import').replace('process.env.SDK_VERSION', `"${sdkPkg.version}"`);
+ content.replace(/^import/m, '// import').replace('process.env.SDK_VERSION', `"${sdkPkg.version}"`);π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const transformVendorsModule = (/** @type {string} */ content) => | |
| 'const modulesService = { getUrl: (mod) => mod };\n' + | |
| content.replace('import', '// import').replace('process.env.SDK_VERSION', `"${sdkPkg.version}"`); | |
| const transformVendorsModule = (/** @type {string} */ content) => | |
| 'const modulesService = { getUrl: (mod) => mod };\n' + | |
| content | |
| .replace(/^import/m, '// import') | |
| .replace('process.env.SDK_VERSION', `"${sdkPkg.version}"`); |
π€ Prompt for AI Agents
In scripts/download-modules.js around lines 13 to 15, the current replacement of
the word "import" with "// import" is too broad and may replace occurrences
inside strings or comments unintentionally. To fix this, update the code to
parse the content properly and only comment out actual import statements, for
example by using a regular expression that matches import statements at the
start of lines or by using a JavaScript parser to identify import declarations
before replacing them.
| const getLocalUrl = (modName: string, modulesBaseUrl = '/modules/') => { | ||
| modName = modName | ||
| .replace('https://unpkg.com/', '') | ||
| .replace('unpkg:', '') | ||
| .replaceAll('https://', '') | ||
| .replaceAll(':', '_') | ||
| .replaceAll('?', '_'); | ||
| if (modName.includes('pyodide')) { | ||
| modName = modName.replace('cdn.jsdelivr.net/', ''); | ||
| } | ||
| return `${modulesBaseUrl}${modName}`; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π‘ Verification agent
π§© Analysis chain
Consider edge cases in URL transformation
The getLocalUrl function handles URL transformations well, but ensure that the special handling for pyodide URLs doesn't accidentally match other URLs containing "pyodide" as a substring.
π Script executed:
#!/bin/bash
# Description: Check for other URLs that might contain "pyodide" and could be affected
# Search for URLs containing "pyodide" in the vendors file
rg -i "pyodide" src/livecodes/vendors.ts -A 2 -B 2Length of output: 164
Narrow pyodide URL matching in getLocalUrl
Use a more precise condition to avoid accidentally matching any module name that merely contains βpyodide.β For example:
β’ File: src/livecodes/services/modules.ts, function getLocalUrl
β’ Update the pyodide check from a substring match to a pathβspecific check:
- if (modName.includes('pyodide')) {
- modName = modName.replace('cdn.jsdelivr.net/', '');
- }
+ // Only strip the JSDelivr prefix when the URL is actually under /pyodide/
+ if (modName.startsWith('cdn.jsdelivr.net/pyodide/')) {
+ modName = modName.replace('cdn.jsdelivr.net/', '');
+ }This change ensures you only transform true pyodide CDN URLs, preventing false positives on other modules whose names may include βpyodide.β
π€ Prompt for AI Agents
In src/livecodes/services/modules.ts around lines 121 to 132, the current
pyodide URL check uses a broad substring match that can incorrectly match module
names containing "pyodide". Refine this check to specifically detect pyodide CDN
URLs by matching the exact path or hostname pattern instead of a simple
substring. Update the condition to only apply the replacement when the URL
clearly corresponds to the pyodide CDN, preventing unintended modifications of
other module names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
β»οΈ Duplicate comments (3)
scripts/download-modules.js (3)
43-44: Fix typo in variable nameThere's a typo in the variable name
verdorModulesContent- it should bevendorModulesContent.- const verdorModulesContent = transformVendorsModule(fs.readFileSync(srcVendorsModule, 'utf8')); - fs.writeFileSync(cacheVendorsModule, verdorModulesContent, 'utf8'); + const vendorModulesContent = transformVendorsModule(fs.readFileSync(srcVendorsModule, 'utf8')); + fs.writeFileSync(cacheVendorsModule, vendorModulesContent, 'utf8');
13-15: Improve import statement replacement to avoid unintended replacementsThe current string replacement might inadvertently replace the word "import" in other contexts (e.g., within strings or comments).
const transformVendorsModule = (/** @type {string} */ content) => 'const modulesService = { getUrl: (mod) => mod };\n' + - content.replace('import', '// import').replace('process.env.SDK_VERSION', `"${sdkPkg.version}"`); + content.replace(/^import/m, '// import').replace('process.env.SDK_VERSION', `"${sdkPkg.version}"`);
198-199: Fix undefined variable in error messageThe error message references an undefined
modulevariable. Should usefileinstead.- console.error(`Failed to download module (${module}): ${result.message}`); + console.error(`Failed to download module (${file}): ${result.message}`);
π§Ή Nitpick comments (3)
scripts/download-modules.js (3)
65-67: Improve robustness of URL manipulationThe string replacement for
@seth0x41/doppiomight fail if the URL structure changes or if similar patterns exist elsewhere.- if (baseUrl.includes('@seth0x41/doppio')) { - baseUrl = baseUrl.replace('https://unpkg.com/', '').replace('unpkg:', ''); - } + if (baseUrl.includes('@seth0x41/doppio')) { + baseUrl = baseUrl.replace(/^https:\/\/unpkg\.com\//, '').replace(/^unpkg:/, ''); + }
149-173: Optimize font file processingThe font file processing logic is duplicated for different font providers. Consider extracting this into a reusable function.
+ const processFontCSS = (content, fullPath, originalDomain, relativePath) => { + const urlPattern = /https:\/\/[^'"\)]*/g; + const fontUrls = Array.from(content.matchAll(new RegExp(urlPattern))).flat(); + const promises = fontUrls.map(fontUrl => { + const fontPath = fontUrl.replace('https://', modulesDir); + return fetchAndSaveFile(fontUrl, fontPath); + }); + const patched = content.replaceAll(originalDomain, relativePath); + fs.writeFileSync(fullPath, patched); + return Promise.all(promises); + }; + if (fullPath.includes('fonts.googleapis.com/css')) { const content = fs.readFileSync(fullPath, 'utf8'); - const fontUrls = Array.from(content.matchAll(new RegExp(urlPattern))).flat(); - for (const fontUrl of fontUrls) { - const fontPath = fontUrl.replace('https://', modulesDir); - await fetchAndSaveFile(fontUrl, fontPath); - } - const patched = content.replaceAll( - 'https://fonts.gstatic.com/', - '../fonts.gstatic.com/', - ); - fs.writeFileSync(fullPath, patched); + await processFontCSS(content, fullPath, 'https://fonts.gstatic.com/', '../fonts.gstatic.com/'); } if (fullPath.includes('fonts.cdnfonts.com/css')) { const content = fs.readFileSync(fullPath, 'utf8'); - const fontUrls = Array.from(content.matchAll(new RegExp(urlPattern))).flat(); - for (const fontUrl of fontUrls) { - const fontPath = fontUrl.replace('https://', modulesDir); - await fetchAndSaveFile(fontUrl, fontPath); - } - const patched = content.replaceAll('https://fonts.cdnfonts.com/', '../'); - fs.writeFileSync(fullPath, patched); + await processFontCSS(content, fullPath, 'https://fonts.cdnfonts.com/', '../'); }
286-341: Consider using existing queue librariesThe custom async queue implementation is well-written but could be replaced with established libraries like
p-queuefor better maintainability and features.
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (1)
scripts/download-modules.js(1 hunks)
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: type-check (18.x)
- GitHub Check: tests (18.x, 2)
- GitHub Check: tests (18.x, 1)
- GitHub Check: tests (18.x, 3)
- GitHub Check: build (18.x)
- GitHub Check: tests (18.x, 5)
- GitHub Check: tests (18.x, 4)
- GitHub Check: type-check (18.x)
- GitHub Check: build (18.x)
|
|
||
| // download Pyodide | ||
| if (pyodideBaseUrl) { | ||
| const pyodideVersion = pyodideBaseUrl.split('/v')[1].split('/')[0] || '0.28.0'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion
Add validation for Pyodide version extraction
The version extraction logic could fail if the URL format changes, leading to incorrect version handling.
- const pyodideVersion = pyodideBaseUrl.split('/v')[1].split('/')[0] || '0.28.0';
+ const versionMatch = pyodideBaseUrl.match(/\/v([^\/]+)/);
+ const pyodideVersion = versionMatch ? versionMatch[1] : '0.28.0';π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const pyodideVersion = pyodideBaseUrl.split('/v')[1].split('/')[0] || '0.28.0'; | |
| const versionMatch = pyodideBaseUrl.match(/\/v([^\/]+)/); | |
| const pyodideVersion = versionMatch ? versionMatch[1] : '0.28.0'; |
π€ Prompt for AI Agents
In scripts/download-modules.js at line 183, the current Pyodide version
extraction from the URL assumes a fixed format and may fail if the URL structure
changes. Add validation to check if the split operations successfully extract a
valid version string before using it. If the extraction fails, fallback to a
default version like '0.28.0' to ensure robust version handling.
| if (require.main === module) { | ||
| downloadModules({ dryRun: process.argv.includes('--dry-run') }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion
Add proper exit handling
The script should handle the async operation completion properly and exit with appropriate codes.
if (require.main === module) {
- downloadModules({ dryRun: process.argv.includes('--dry-run') });
+ downloadModules({ dryRun: process.argv.includes('--dry-run') })
+ .then(() => process.exit(0))
+ .catch(err => {
+ console.error('Script failed:', err);
+ process.exit(1);
+ });
}π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (require.main === module) { | |
| downloadModules({ dryRun: process.argv.includes('--dry-run') }); | |
| } | |
| if (require.main === module) { | |
| downloadModules({ dryRun: process.argv.includes('--dry-run') }) | |
| .then(() => process.exit(0)) | |
| .catch(err => { | |
| console.error('Script failed:', err); | |
| process.exit(1); | |
| }); | |
| } |
π€ Prompt for AI Agents
In scripts/download-modules.js around lines 345 to 347, the script calls the
async function downloadModules but does not handle its completion or errors
properly. Modify the code to await the downloadModules call, then explicitly
call process.exit(0) on success and process.exit(1) on failure, ensuring the
script exits with appropriate status codes after the async operation finishes.
| const fullPath = | ||
| modulesDir + module.replaceAll('https://', '').replaceAll(':', '_').replaceAll('?', '_'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion
Improve file path sanitization
The current path sanitization using replaceAll might not handle all edge cases and could create naming conflicts.
- const fullPath =
- modulesDir + module.replaceAll('https://', '').replaceAll(':', '_').replaceAll('?', '_');
+ const sanitizedModule = module
+ .replace(/^https?:\/\//, '')
+ .replace(/[<>:"|?*]/g, '_')
+ .replace(/\/+/g, '/');
+ const fullPath = path.join(modulesDir, sanitizedModule);π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const fullPath = | |
| modulesDir + module.replaceAll('https://', '').replaceAll(':', '_').replaceAll('?', '_'); | |
| const sanitizedModule = module | |
| .replace(/^https?:\/\//, '') | |
| .replace(/[<>:"|?*]/g, '_') | |
| .replace(/\/+/g, '/'); | |
| const fullPath = path.join(modulesDir, sanitizedModule); |
π€ Prompt for AI Agents
In scripts/download-modules.js around lines 127 to 128, the current file path
sanitization uses multiple replaceAll calls which may not cover all edge cases
and could lead to naming conflicts. Replace this with a more robust sanitization
approach by using a single regular expression to replace all characters that are
invalid or problematic in file paths (such as slashes, colons, question marks,
and other special characters) with underscores or another safe character to
ensure unique and valid file names.
scripts/download-modules.js
Outdated
| const modInfoUrl = `https://data.jsdelivr.com/v1/package/${type}/${mod}/flat`; | ||
| const modInfo = await fetch(modInfoUrl).then((res) => res.json()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion
Add error handling for API requests
The jsDelivr API request lacks error handling, which could cause the script to fail silently or crash.
- const modInfoUrl = `https://data.jsdelivr.com/v1/package/${type}/${mod}/flat`;
- const modInfo = await fetch(modInfoUrl).then((res) => res.json());
+ const modInfoUrl = `https://data.jsdelivr.com/v1/package/${type}/${mod}/flat`;
+ const response = await fetch(modInfoUrl);
+ if (!response.ok) {
+ console.warn(`Failed to fetch module info for ${mod}: ${response.status}`);
+ continue;
+ }
+ const modInfo = await response.json();π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const modInfoUrl = `https://data.jsdelivr.com/v1/package/${type}/${mod}/flat`; | |
| const modInfo = await fetch(modInfoUrl).then((res) => res.json()); | |
| const modInfoUrl = `https://data.jsdelivr.com/v1/package/${type}/${mod}/flat`; | |
| const response = await fetch(modInfoUrl); | |
| if (!response.ok) { | |
| console.warn(`Failed to fetch module info for ${mod}: ${response.status}`); | |
| continue; | |
| } | |
| const modInfo = await response.json(); |
π€ Prompt for AI Agents
In scripts/download-modules.js around lines 75 to 76, the fetch call to the
jsDelivr API does not have error handling, which may cause the script to fail
silently or crash if the request fails. Wrap the fetch call in a try-catch block
or use .catch to handle errors gracefully. In case of an error, log a meaningful
message and handle the failure appropriately to prevent the script from crashing
unexpectedly.
Docstrings generation was requested by @hatemhosny. * #858 (comment) The following files were modified: * `scripts/download-modules.js` * `src/livecodes/languages/python-wasm/lang-python-wasm-script.ts` * `src/livecodes/types/bundle-types.ts`
β¦ into local-modules
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 23
Caution
Some comments are outside the diff and canβt be posted inline due to platform limitations.
β οΈ Outside diff range comments (1)
src/livecodes/html/language-info.html (1)
201-201: Broken C++ link: use isocpp.org (not isoclang.org)Fix the Standard C++ Foundation link.
Apply:
- <a href="https://isoclang.org/" target="_blank" rel="noopener">Standard C++ Foundation</a> + <a href="https://isocpp.org/" target="_blank" rel="noopener">Standard C++ Foundation</a>
π§Ή Nitpick comments (7)
src/livecodes/core.ts (1)
1435-1443: Use combinedConfig when updating editors (clarity/consistency)Function already computes combinedConfig; passing it avoids relying on external setConfig state.
- if (reload) { - await updateEditors(editors, getConfig()); - } + if (reload) { + await updateEditors(editors, combinedConfig); + }This should be a no-op today but makes the function self-contained against future callers.
src/livecodes/i18n/locales/en/language-info.lokalise.json (1)
299-310: New Go (Wasm) entries β minor consistency nits.
- Name casing across file alternates between βWasmβ and βWASMβ; consider standardizing (e.g., βWasmβ) for all languages.
- Several entries in this file use βLiveCodes Documentationsβ; consider βLiveCodes Documentationβ for English.
If you want, I can open a follow-up PR to harmonize casing/wording across locales.
src/livecodes/vendors.ts (2)
122-123: doppioJvmBaseUrl moved to unpkg β good, but add fallback if CDN blocked.Given LOCAL_MODULES goals, ensure scripts/download-modules.js picks this up and caches it properly from unpkg.
437-441: Use a stable Tailwind v4 or stick to v3 until v4 is fully validated
Tailwind 4.0.0 is published on npm (Jan 21, 2025) and serves from jsDelivr (200 OK) but isnβt the latest stable release (v4.1.13 is tagged βlatestβ). Either bump to the latest v4 patch or remain on 3.4.x behind a feature flag until youβve confirmed full v4 compatibility.- export const tailwindcssBaseUrl = /* @__PURE__ */ getUrl('[email protected]/'); + // for v4: update to latest patch + export const tailwindcssBaseUrl = /* @__PURE__ */ getUrl('[email protected]/');docs/src/components/TemplateList.tsx (1)
78-81: Harden external link and improve image accessibilityAdd rel to target _blank, alt text, and lazy loading to thumbnails.
- <a href={appUrl + '/?template=' + t.name} target="_blank"> - <img src={appUrl + '/livecodes/assets/templates/' + t.thumbnail} /> + <a href={appUrl + '/?template=' + t.name} target="_blank" rel="noopener noreferrer"> + <img + src={appUrl + '/livecodes/assets/templates/' + t.thumbnail} + alt={t.title} + loading="lazy" + />src/livecodes/i18n/locales/fr/language-info.lokalise.json (1)
225-233: Minor FR copy consistencyPrefer βApprenez β¦β and βle modΓ¨leβ to match existing entries.
- "goWasm.link": { - "translation": "<tag-1><tag-2>Site officiel de Go</tag-2></tag-1> <tag-3><tag-4>Yaegi</tag-4></tag-3> <tag-5> <tag-6>Apprendre X en Y minutes, où X=Go</tag-6> </tag-5> <tag-7> <tag-8>LiveCodes Documentations</tag-8> </tag-7> <tag-9> <tag-10>Charger un modèle de démarrage</tag-10> </tag-9>" - }, + "goWasm.link": { + "translation": "<tag-1><tag-2>Site officiel de Go</tag-2></tag-1> <tag-3><tag-4>Yaegi</tag-4></tag-3> <tag-5> <tag-6>Apprenez X en Y minutes, où X=Go</tag-6> </tag-5> <tag-7> <tag-8>Documentations LiveCodes</tag-8> </tag-7> <tag-9> <tag-10>Charger le modèle de démarrage</tag-10> </tag-9>" + },src/livecodes/templates/starter/go-wasm-starter.ts (1)
45-53: Avoid overlapping runs; disable/enable buttons while executingPrevent rapid clicks from starting concurrent runs and give clearer UX.
- incrementBtn.onclick = async () => { + incrementBtn.onclick = async () => { + incrementBtn.disabled = true; const currentCount = document.querySelector("#counter").textContent; const {output, error} = await livecodes.goWasm.run(currentCount); if (error) { console.error('Error:', error); } else { document.querySelector("#counter").textContent = output; } + incrementBtn.disabled = false; }; - greetBtn.onclick = async () => { + greetBtn.onclick = async () => { + greetBtn.disabled = true; const name = document.querySelector("#name-input").value; if (!name.trim()) { alert('Please enter your name'); - return; + return (greetBtn.disabled = false); } const {output, error} = await livecodes.goWasm.run(name); if (error) { console.error('Error:', error); } else { document.querySelector("#greeting").textContent = output; } + greetBtn.disabled = false; };Also applies to: 55-67
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (83)
README.md(1 hunks)docs/docs/languages/go-wasm.mdx(1 hunks)docs/docs/languages/go.mdx(1 hunks)docs/docs/languages/markdown.mdx(1 hunks)docs/docs/languages/mdx.mdx(1 hunks)docs/src/components/LanguageSliders.tsx(1 hunks)docs/src/components/TemplateList.tsx(1 hunks)scripts/build.js(4 hunks)src/livecodes/UI/command-menu-actions.ts(1 hunks)src/livecodes/compiler/compile-blocks.ts(1 hunks)src/livecodes/config/__tests__/build-config.spec.ts(1 hunks)src/livecodes/config/__tests__/validate-config.spec.ts(1 hunks)src/livecodes/config/build-config.ts(1 hunks)src/livecodes/config/validate-config.ts(1 hunks)src/livecodes/core.ts(8 hunks)src/livecodes/editor/monaco/monaco.ts(3 hunks)src/livecodes/html/language-info.html(1 hunks)src/livecodes/i18n/locales/ar/language-info.lokalise.json(1 hunks)src/livecodes/i18n/locales/ar/language-info.ts(1 hunks)src/livecodes/i18n/locales/ar/translation.lokalise.json(2 hunks)src/livecodes/i18n/locales/ar/translation.ts(1 hunks)src/livecodes/i18n/locales/de/language-info.lokalise.json(1 hunks)src/livecodes/i18n/locales/de/language-info.ts(1 hunks)src/livecodes/i18n/locales/de/translation.lokalise.json(2 hunks)src/livecodes/i18n/locales/de/translation.ts(1 hunks)src/livecodes/i18n/locales/en/language-info.lokalise.json(1 hunks)src/livecodes/i18n/locales/en/language-info.ts(1 hunks)src/livecodes/i18n/locales/en/translation.lokalise.json(2 hunks)src/livecodes/i18n/locales/en/translation.ts(1 hunks)src/livecodes/i18n/locales/es/language-info.lokalise.json(1 hunks)src/livecodes/i18n/locales/es/language-info.ts(1 hunks)src/livecodes/i18n/locales/es/translation.lokalise.json(2 hunks)src/livecodes/i18n/locales/es/translation.ts(1 hunks)src/livecodes/i18n/locales/fa/language-info.lokalise.json(1 hunks)src/livecodes/i18n/locales/fa/language-info.ts(1 hunks)src/livecodes/i18n/locales/fa/translation.lokalise.json(2 hunks)src/livecodes/i18n/locales/fa/translation.ts(1 hunks)src/livecodes/i18n/locales/fr/language-info.lokalise.json(1 hunks)src/livecodes/i18n/locales/fr/language-info.ts(1 hunks)src/livecodes/i18n/locales/fr/translation.lokalise.json(2 hunks)src/livecodes/i18n/locales/fr/translation.ts(1 hunks)src/livecodes/i18n/locales/hi/language-info.lokalise.json(1 hunks)src/livecodes/i18n/locales/hi/language-info.ts(1 hunks)src/livecodes/i18n/locales/hi/translation.lokalise.json(2 hunks)src/livecodes/i18n/locales/hi/translation.ts(1 hunks)src/livecodes/i18n/locales/it/language-info.lokalise.json(1 hunks)src/livecodes/i18n/locales/it/language-info.ts(1 hunks)src/livecodes/i18n/locales/it/translation.lokalise.json(2 hunks)src/livecodes/i18n/locales/it/translation.ts(1 hunks)src/livecodes/i18n/locales/ja/language-info.lokalise.json(1 hunks)src/livecodes/i18n/locales/ja/language-info.ts(1 hunks)src/livecodes/i18n/locales/ja/translation.lokalise.json(2 hunks)src/livecodes/i18n/locales/ja/translation.ts(1 hunks)src/livecodes/i18n/locales/pt/language-info.lokalise.json(1 hunks)src/livecodes/i18n/locales/pt/language-info.ts(1 hunks)src/livecodes/i18n/locales/pt/translation.lokalise.json(2 hunks)src/livecodes/i18n/locales/pt/translation.ts(1 hunks)src/livecodes/i18n/locales/ru/language-info.lokalise.json(1 hunks)src/livecodes/i18n/locales/ru/language-info.ts(1 hunks)src/livecodes/i18n/locales/ru/translation.lokalise.json(2 hunks)src/livecodes/i18n/locales/ru/translation.ts(1 hunks)src/livecodes/i18n/locales/ur/language-info.lokalise.json(1 hunks)src/livecodes/i18n/locales/ur/language-info.ts(1 hunks)src/livecodes/i18n/locales/ur/translation.lokalise.json(2 hunks)src/livecodes/i18n/locales/ur/translation.ts(1 hunks)src/livecodes/i18n/locales/zh-CN/language-info.lokalise.json(1 hunks)src/livecodes/i18n/locales/zh-CN/language-info.ts(1 hunks)src/livecodes/i18n/locales/zh-CN/translation.lokalise.json(2 hunks)src/livecodes/i18n/locales/zh-CN/translation.ts(1 hunks)src/livecodes/languages/go-wasm/index.ts(1 hunks)src/livecodes/languages/go-wasm/lang-go-wasm-script.ts(1 hunks)src/livecodes/languages/go-wasm/lang-go-wasm.ts(1 hunks)src/livecodes/languages/languages.ts(2 hunks)src/livecodes/languages/tailwindcss/processor-tailwindcss-compiler.ts(2 hunks)src/livecodes/languages/tailwindcss/utils.ts(1 hunks)src/livecodes/templates/starter/cpp-wasm-starter.ts(1 hunks)src/livecodes/templates/starter/go-wasm-starter.ts(1 hunks)src/livecodes/templates/starter/index.ts(2 hunks)src/livecodes/utils/utils.ts(1 hunks)src/livecodes/vendors.ts(13 hunks)src/sdk/models.ts(4 hunks)src/sdk/vue.ts(1 hunks)vendor-licenses.md(1 hunks)
β Files skipped from review due to trivial changes (3)
- vendor-licenses.md
- docs/docs/languages/go-wasm.mdx
- README.md
π§ Files skipped from review as they are similar to previous changes (1)
- src/livecodes/editor/monaco/monaco.ts
π§° Additional context used
𧬠Code graph analysis (12)
src/livecodes/config/__tests__/build-config.spec.ts (2)
src/livecodes/config/build-config.ts (1)
loadParamConfig(147-386)src/livecodes/config/default-config.ts (1)
defaultConfig(3-73)
src/livecodes/languages/go-wasm/lang-go-wasm.ts (2)
src/sdk/models.ts (1)
LanguageSpecs(1197-1210)scripts/build.js (1)
baseUrl(40-40)
src/livecodes/languages/tailwindcss/processor-tailwindcss-compiler.ts (3)
src/livecodes/languages/tailwindcss/utils.ts (1)
addCodeInStyleBlocks(1-13)src/sdk/models.ts (1)
CompilerFunction(1307-1322)src/livecodes/compiler/import-map.ts (1)
replaceStyleImports(244-267)
src/livecodes/languages/go-wasm/lang-go-wasm-script.ts (2)
src/livecodes/vendors.ts (1)
yaegiWasmBaseUrl(110-110)src/livecodes/utils/utils.ts (1)
createWorkerFromContent(291-297)
src/livecodes/templates/starter/go-wasm-starter.ts (1)
src/sdk/models.ts (1)
Template(1374-1381)
src/livecodes/templates/starter/index.ts (1)
src/livecodes/templates/starter/go-wasm-starter.ts (1)
goWasmStarter(3-177)
src/livecodes/config/validate-config.ts (1)
src/sdk/models.ts (1)
Editor(1093-1166)
src/livecodes/languages/languages.ts (1)
src/livecodes/languages/go-wasm/lang-go-wasm.ts (1)
goWasm(4-19)
src/livecodes/config/build-config.ts (2)
src/livecodes/main.ts (1)
params(12-12)src/livecodes/utils/utils.ts (1)
addProp(673-686)
scripts/build.js (1)
scripts/i18n.js (2)
path(3-3)outDir(7-7)
src/livecodes/core.ts (2)
src/sdk/models.ts (3)
EditorConfig(710-819)Config(406-406)CodeEditor(1521-1558)src/livecodes/config/config.ts (3)
getEditorConfig(70-87)getFormatterConfig(89-96)getConfig(15-15)
src/livecodes/vendors.ts (1)
src/livecodes/services/modules.ts (1)
modulesService(35-87)
πͺ ast-grep (0.39.5)
src/livecodes/languages/tailwindcss/utils.ts
[warning] 5-5: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(pattern, 'g')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: Redirect rules - livecodes
- GitHub Check: Header rules - livecodes
- GitHub Check: Pages changed - livecodes
- GitHub Check: Codacy Static Code Analysis
- GitHub Check: tests (18.x, 5)
- GitHub Check: tests (18.x, 4)
- GitHub Check: build
- GitHub Check: tests (18.x, 3)
- GitHub Check: tests (18.x, 2)
- GitHub Check: tests (18.x, 1)
- GitHub Check: build (18.x)
- GitHub Check: build (18.x)
π Additional comments (47)
src/livecodes/i18n/locales/ur/translation.lokalise.json (1)
1953-1955: Translations look accurate.The new Urdu strings follow the existing naming pattern for starter templates (e.g., Lua/Golang Wasm entries) and keep the same terminology for βWasmβ and βΨ΄Ψ±ΩΨΉΨ§ΨͺΫ.β Nicely consistent.
Also applies to: 1971-1973
src/livecodes/i18n/locales/pt/language-info.ts (1)
130-134: Go (Wasm) entry looks goodThe new Go (Wasm) localization reads naturally and mirrors the surrounding entries.
src/livecodes/i18n/locales/ja/translation.ts (1)
993-999: Starter labels match the new wasm entriesThanks for adding the JP labels for the Wasm starters; they line up with the rest of the template list.
src/sdk/vue.ts (1)
124-130: Great fix fordata-heightbindingBinding the attribute to
height.valueavoids emitting[object Object]and keeps the Vue wrapper aligned with how the SDK expectsdata-heightto work for sizing. (livecodes.io)docs/docs/languages/mdx.mdx (1)
3-108: Appreciate the richer MDX docs & demoThe expanded explanation plus the embedded LiveCodes example make the page much more helpful.
src/livecodes/i18n/locales/de/translation.lokalise.json (1)
1953-1973: German starter labels updated consistentlyNice job adding the new Wasm starter strings in sync with the rest of the locale file.
docs/docs/languages/go.mdx (1)
3-73: Great refresh of the Go docs.The new demo, usage guidance, and Go (Wasm) cross-link make this page immediately useful for readers.
src/livecodes/i18n/locales/hi/language-info.ts (1)
130-134: Hindi goWasm entry looks consistent.The new strings wire into the auto-generated locale structure without breaking placeholder patterns.
src/livecodes/i18n/locales/it/language-info.ts (1)
130-134: Italian goWasm strings slot in cleanly.Localization metadata matches existing entries, so the starter catalog will surface correctly.
src/livecodes/i18n/locales/de/translation.ts (1)
994-1001: Starter template additions read well.Both new labels follow the existing German phrasing pattern for Wasm starters.
src/livecodes/config/validate-config.ts (1)
141-149: Nice consolidation of editor validation.Reusing
validateEditorPropsacross markup/style/script keeps normalization uniform and trims duplicate checks.src/livecodes/config/__tests__/validate-config.spec.ts (1)
160-161: Test expectation update looks rightMatches the validatorβs normalization to the default JS script editor. No further changes needed here.
scripts/build.js (1)
235-236: Nice: include goβwasm IIFE entryConfirms assets are bundled for the new language.
Please confirm the file exists and builds: src/livecodes/languages/go-wasm/lang-go-wasm-script.ts.
src/livecodes/core.ts (6)
102-104: Exported EditorConfig import looks goodAligns core with the public SDK types.
532-533: Initialize merged editor+formatter config β OKGood place to establish the baseline for changeSettings calls.
1521-1534: Editor reload heuristic and delta detection β sensible
- Checking editor backend switch and mode transitions is sound.
- Tailwind toggle handling below also makes sense.
No change requested.
Edge case: if newConfig.editor is 'auto', confirm it wonβt spuriously reload when current backend already matches autoβs resolution.
1550-1555: Propagate settings to all editors, including tools β good; ensure type compatibilityAfter widening currentEditorConfig type (above), this becomes type-safe and futureβproof.
If any editor ignores formatter keys, confirm changeSettings safely drops unknown fields.
1972-1977: getAllEditors utility β LGTMCovers console/compiled editors defensively.
5568-5569: applyConfig(oldConfig) usage β good API designPassing oldConfig enables precise deltas. Matches the updated signature.
src/livecodes/i18n/locales/en/language-info.lokalise.json (1)
292-294: Go (GopherJS) links updated β looks consistent.Tag placeholders match notes; URLs and labels LGTM.
src/livecodes/i18n/locales/pt/language-info.lokalise.json (1)
225-233: Go (Wasm) PT translations added β LGTM.Wording reads naturally; tag order matches notes. No blocking issues.
src/livecodes/templates/starter/index.ts (1)
24-24: Approve Go (Wasm) starter registration
TemplateName correctly includes 'go-wasm'; import path and registration order (Go > Go (Wasm)) are correct. Also applies to lines 115-116.src/livecodes/vendors.ts (4)
360-363: Verify Pyodide v0.28.0 bump compatibility across init, package loading, and FS APIs.Confirm that:
loadPyodide({ indexURL: pyodideBaseUrl })still loadspyodide.jsas expected.livecodes.pyodide.loadPackage('micropip')and subsequent imports remain correct.- Fallback from
runPythonAsynctorunPython(v0.26.x workaround) still applies.- All Pyodide FS API calls (e.g.
FS.mount,FS.writeFile,FS.readdir) align with v0.28.0 changes.
140-246: Confirm download-modules handles?style.cssCSS URLs correctly
HEAD checks on sample Google- and CDN-hosted font URLs return 200 withcontent-type: text/cssand appropriate CORS. Ensure the buildβs download-modules/getUrl step strips or normalizes query parameters so that cached files end in.css, are served withtext/css, and support offline loading.
96-96: All imports updated; no stalecodeMirrorBaseUrlreferences remain. Review comment resolved.
390-401: ReScript CDN loader now fetches all four URLs in correct order Verified thatlang-rescript-compiler-esm.tsloads[rescriptCdnUrl1, rescriptCdnUrl2, rescriptCdnUrl3, rescriptCdnUrl4]and the formatter correctly imports only the compiler.src/livecodes/i18n/locales/fr/translation.ts (1)
994-1001: FR starter keys added correctlyEntries for 'cpp-wasm' and 'go-wasm' match naming across locales. LGTM.
docs/src/components/TemplateList.tsx (1)
45-46: Add goβwasm starter: looks goodPlacement next to Go is consistent with other wasm variants.
src/livecodes/i18n/locales/zh-CN/translation.lokalise.json (1)
1953-1955: zh-CN wasm starters addedKeys and casing align with existing βLuaοΌWASMοΌβ style. LGTM.
Also applies to: 1971-1973
src/livecodes/i18n/locales/pt/translation.lokalise.json (1)
1953-1955: PT wasm starters addedTranslations match existing βLua (Wasm)β pattern. LGTM.
Also applies to: 1971-1973
src/livecodes/config/__tests__/build-config.spec.ts (1)
508-515: Good coverage for dotted params; add parse-path testThis validates loadParamConfig addProp behavior. Add a companion test using getParams to ensure booleans in dotted keys are parsed as expected from query strings.
+ test('?markup.hideTitle=true parsed via getParams', () => { + const output: Partial<Config> = loadParamConfig( + defaultConfig, + getParams('?markup.hideTitle=true&script.title=App.jsx'), + ); + expect(output.markup?.hideTitle).toBe(true); + expect(output.script?.title).toBe('App.jsx'); + });src/livecodes/i18n/locales/en/translation.ts (1)
999-1006: LGTM: adds Wasm starter labels.Keys and labels align with existing conventions (hyphenated keys, β(Wasm)β suffix).
src/livecodes/i18n/locales/it/translation.lokalise.json (1)
1953-1955: Approve IT Wasm starter translations. C++ (Wasm) and Go (Wasm) starter keys are present and consistent across all locales.src/livecodes/config/build-config.ts (1)
368-382: Expanded parameter propagation looks solidThe dot-notation handling now covers markup/style/script/tests/customSettings/imports/types/tools prefixes, and reusing
addPropcleanly maps nested keys without disrupting the earlier config assembly logic.src/livecodes/i18n/locales/hi/translation.lokalise.json (1)
1953-1973: Consistent starter template additions β The newcpp-wasmandgo-wasmentries mirror the existing naming/translation pattern for Hindi starters, so we stay aligned with the auto-generated locale structure.src/livecodes/i18n/locales/ar/translation.lokalise.json (1)
1953-1973: Arabic locale keeps parity β Addingcpp-wasmandgo-wasmwith localized strings maintains parity with other locales and fits the generated fileβs ordering.src/livecodes/i18n/locales/zh-CN/translation.ts (1)
990-997: zh-CN starter labels look good β The new'cpp-wasm'/'go-wasm'translations are properly localized, keyed, and positioned alongside existing WASM starters in this generated map.src/livecodes/i18n/locales/ar/translation.ts (1)
992-999: Arabic starter catalog stays synchronized β The Wasm variants are inserted with consistent naming and translation, keeping the generated TypeScript map aligned with the JSON source.src/livecodes/i18n/locales/ru/translation.ts (1)
992-999: Russian locale parity achieved βcpp-wasmandgo-wasmfollow the established starter pattern, so the generated Russian map remains complete and ordered.src/livecodes/i18n/locales/es/translation.lokalise.json (1)
1953-1955: Translations align with existing starter labels
The added Spanish strings keep the βInicio β¦β pattern and correctly surface the new Wasm templates.Also applies to: 1971-1973
src/livecodes/i18n/locales/de/language-info.ts (1)
131-135: goWasm entry reads well
Copy mirrors the neighbouring entries and keeps placeholders intact, so the German locale stays consistent.src/livecodes/i18n/locales/fr/translation.lokalise.json (1)
1953-1955: French starter labels stay consistent
The new Wasm variants follow the same βDΓ©marrage β¦β convention and plug straight into the templates list.Also applies to: 1971-1973
src/livecodes/i18n/locales/ru/language-info.ts (1)
132-135: Russian goWasm copy looks good
Terminology, placeholder ordering, and style align with the neighbouring entries, so no further tweaks needed.src/livecodes/html/language-info.html (2)
537-541: Docs link for Go added β OKThe LiveCodes docs link for Go matches the pattern used elsewhere. LGTM.
549-573: New Go (Wasm) section β OKStructure, i18n keys, and starter link align with other languages. LGTM.
src/livecodes/i18n/locales/hi/translation.ts (1)
992-999: Wasm starter keys present in all localesVerified that
templates.starter.cpp-wasmandtemplates.starter.go-wasmexist across every locale. LGTM.src/livecodes/languages/go-wasm/lang-go-wasm-script.ts (1)
97-106: Drop the redundant one-liner refactor. The current code already postsloadedonly afterinitYaegi()resolves, so no behavior change is needed.Likely an incorrect or invalid review comment.
| By default, no styes are added. Only HTML output is generated from the Markdown code. | ||
|
|
||
| If you want to style the result page similar to GitHub Markdown, you can use [`github-markdown-css`](https://github.com/sindresorhus/github-markdown-css). | ||
| Note that the body needs to have a `class="markdown-body"` for the styles to be applied. | ||
|
|
||
| ```js title="Script Editor (JS)" | ||
| document.body.classList.add('markdown-body'); | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix typo in Styles section.
Line 69 should read βstylesβ instead of βstyes.β Please correct the typo to keep the docs polished.
π€ Prompt for AI Agents
In docs/docs/languages/markdown.mdx around lines 69 to 76, there's a typo:
"styes" should be "styles"; update the text to read "By default, no styles are
added." (preserve the rest of the paragraph and surrounding content exactly).
| const useSelfHostedURL = (/** @type {string} */ content) => { | ||
| if (!process.env.HOST_NAME) return content; | ||
| const hostname = process.env.HOST_NAME; | ||
| const port = Number(process.env.PORT) || 443; | ||
| const appUrl = `https://${hostname}${port !== 443 ? ':' + port : ''}`; | ||
| return content.replaceAll('https://livecodes.io', `${appUrl}`); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion | π Major
π§© Analysis chain
Donβt force HTTPS; build a robust origin from env (HOST_NAME/PROTOCOL/PORT)
Current logic breaks typical Docker localhost (http:80/8080) and double-prefixes if HOST_NAME includes scheme.
Recommend:
-const useSelfHostedURL = (/** @type {string} */ content) => {
- if (!process.env.HOST_NAME) return content;
- const hostname = process.env.HOST_NAME;
- const port = Number(process.env.PORT) || 443;
- const appUrl = `https://${hostname}${port !== 443 ? ':' + port : ''}`;
- return content.replaceAll('https://livecodes.io', `${appUrl}`);
-};
+const useSelfHostedURL = (/** @type {string} */ content) => {
+ const rawHost = (process.env.HOST_NAME || '').trim();
+ if (!rawHost) return content;
+ const hasScheme = /^https?:\/\//i.test(rawHost);
+ const scheme = ((process.env.PROTOCOL || process.env.SCHEME || '').trim().toLowerCase()) || 'http';
+ const port = Number(process.env.PORT);
+ const origin = hasScheme
+ ? rawHost.replace(/\/+$/, '')
+ : `${scheme}://${rawHost}${port && ![80, 443].includes(port) ? ':' + port : ''}`;
+ return content.replaceAll('https://livecodes.io', origin);
+};This covers http dev, https prod, and pre-schemed HOST_NAME.
Run to check remaining hard-coded origins:
π Script executed:
#!/bin/bash
rg -nPI --hidden --glob '!build/**' 'https?://livecodes\.io' -C2Length of output: 148
Running refined searches for hard-coded origins and mkdir usage:
π Script executed:
#!/bin/bash
# Locate build.js path
fd --type f build.js
# Search for hard-coded livecodes.io URLs (excluding node_modules)
rg -nPI --glob '!node_modules/**' 'https?://livecodes\.io' -C2
# Check for any mkdir invocations in scripts
rg -n 'mkdir' -C3 scriptsLength of output: 4997
π Script executed:
#!/bin/bash
# Find manifest writes in build.js
rg -n 'manifest' -C5 scripts/build.js
# Find any file writes to assets in build.js
rg -n 'writeFileSync' -C5 scripts/build.jsLength of output: 1660
Support flexible self-hosted origins and ensure assets directory exists
- Before writing
site.webmanifest(lines 80β87 inscripts/build.js), callmkdir(outDir + '/livecodes/assets')via the existingmkdirutil so theassetsfolder exists. - Refactor
useSelfHostedURLto respect an optionalPROTOCOL/SCHEMEenv var, avoid forcinghttps, handle a pre-schemedHOST_NAME(preventhttp://http://β¦), and only append non-default ports. Example:const useSelfHostedURL = content => { const rawHost = (process.env.HOST_NAME || '').trim(); if (!rawHost) return content; const hasScheme = /^https?:\/\//i.test(rawHost); const scheme = process.env.PROTOCOL?.trim().toLowerCase() || (hasScheme ? rawHost.split('://')[0].toLowerCase() : 'http'); const hostPart = hasScheme ? rawHost.replace(/\/+$/, '') : `${rawHost}${process.env.PORT && !['80','443'].includes(process.env.PORT) ? ':' + process.env.PORT : ''}`; const origin = hasScheme ? hostPart : `${scheme}://${hostPart}`; return content.replaceAll('https://livecodes.io', origin); }; - Verify there are no remaining hard-coded
https://livecodes.ioURLs in the build pipeline.
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const useSelfHostedURL = (/** @type {string} */ content) => { | |
| if (!process.env.HOST_NAME) return content; | |
| const hostname = process.env.HOST_NAME; | |
| const port = Number(process.env.PORT) || 443; | |
| const appUrl = `https://${hostname}${port !== 443 ? ':' + port : ''}`; | |
| return content.replaceAll('https://livecodes.io', `${appUrl}`); | |
| }; | |
| // scripts/build.js | |
| // ... earlier in the file | |
| const useSelfHostedURL = content => { | |
| const rawHost = (process.env.HOST_NAME || '').trim(); | |
| if (!rawHost) return content; | |
| const hasScheme = /^https?:\/\//i.test(rawHost); | |
| const scheme = process.env.PROTOCOL?.trim().toLowerCase() | |
| || (hasScheme ? rawHost.split('://')[0].toLowerCase() : 'http'); | |
| const hostPart = hasScheme | |
| ? rawHost.replace(/\/+$/, '') | |
| : `${rawHost}${process.env.PORT && !['80','443'].includes(process.env.PORT) ? ':' + process.env.PORT : ''}`; | |
| const origin = hasScheme ? hostPart : `${scheme}://${hostPart}`; | |
| return content.replaceAll('https://livecodes.io', origin); | |
| }; | |
| // ... later where site.webmanifest is written | |
| mkdir(outDir + '/livecodes/assets'); | |
| // proceed to write site.webmanifest into outDir + '/livecodes' |
π€ Prompt for AI Agents
In scripts/build.js around lines 53 to 59 and before lines 80β87, the build
currently forces https and may write the manifest without ensuring the assets
directory exists; first call the existing mkdir utility to create outDir +
'/livecodes/assets' before writing site.webmanifest so the assets folder exists;
then refactor useSelfHostedURL to read an optional PROTOCOL (or SCHEME) env var
and use it if present otherwise default to 'https', detect and avoid
double-scheming when HOST_NAME already includes a scheme, construct appUrl as
`${protocol}://${hostname}${portPart}` where portPart is only added when a port
is set and is not the default for that protocol, and replace hard-coded
'https://livecodes.io' occurrences via this function; finally scan the build
pipeline for any remaining hard-coded 'https://livecodes.io' strings and replace
them to use the refactored useSelfHostedURL so all origins are configurable.
| await fs.promises | ||
| .readFile(path.resolve(root, 'src/livecodes/assets/site.webmanifest'), 'utf8') | ||
| .then((siteWebManifest) => | ||
| fs.promises.writeFile( | ||
| path.resolve(outDir, 'livecodes/assets/site.webmanifest'), | ||
| useSelfHostedURL(siteWebManifest), | ||
| 'utf8', | ||
| ), | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure destination directory exists before writing site.webmanifest
Writing to build/livecodes/assets/ will fail if βassetsβ wasnβt created.
await Promise.all([
// add headers
process.env.CF_PAGES ? copyFile('src/_headers', '_headers') : Promise.resolve(),
copyFile('src/netlify.toml', 'netlify.toml'),
copyFile('src/favicon.ico', 'favicon.ico'),
copyFile('src/404.html', '404.html', addBaseUrl),
- copyFile('src/index.html', 'index.html', useSelfHostedURL),
+ copyFile('src/index.html', 'index.html', useSelfHostedURL),
copyFile('src/livecodes/html/app-base.html', 'app.html'),
]);
-await fs.promises
+mkdir(path.resolve(outDir, 'livecodes/assets/'));
+await fs.promises
.readFile(path.resolve(root, 'src/livecodes/assets/site.webmanifest'), 'utf8')
.then((siteWebManifest) =>
fs.promises.writeFile(
path.resolve(outDir, 'livecodes/assets/site.webmanifest'),
useSelfHostedURL(siteWebManifest),
'utf8',
),
);Committable suggestion skipped: line range outside the PR's diff.
π€ Prompt for AI Agents
In scripts/build.js around lines 79 to 87, the code writes
livecodes/assets/site.webmanifest but does not ensure the destination directory
exists; before writing, create the destination directory (path.resolve(outDir,
'livecodes/assets')) using fs.promises.mkdir(..., { recursive: true }) and await
it, then proceed to write the file (or chain the write after the mkdir promise)
so the write cannot fail if the assets directory is missing.
| // do not place compiled css for tailwind and similar in style blocks | ||
| if (['tailwindcss', 'unocss', 'windicss'].includes(processor.name)) continue; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Restore Tailwind/Uno/Windi processing
Skipping these processors means their compilers never run in postProcess, so Tailwind/Uno/Windi CSS is no longer generated or injected. Any project relying on those frameworks now renders without styling, breaking existing functionality and defeating the offline objective. Please keep the processors in the loop (or move their output elsewhere) so their CSS still reaches the runtime.
- // do not place compiled css for tailwind and similar in style blocks
- if (['tailwindcss', 'unocss', 'windicss'].includes(processor.name)) continue;
+ // TODO: adjust downstream handling if we need a different injection point,
+ // but we must still run these processors so their CSS is generated.π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // do not place compiled css for tailwind and similar in style blocks | |
| if (['tailwindcss', 'unocss', 'windicss'].includes(processor.name)) continue; | |
| // TODO: adjust downstream handling if we need a different injection point, | |
| // but we must still run these processors so their CSS is generated. |
| let currentEditorConfig: EditorConfig; | ||
| let toolsPane: ToolsPane | undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion | π Major
π§© Analysis chain
Type currentEditorConfig to include formatter settings too
You merge formatter fields (e.g., semicolons, singleQuote, trailingComma). Widen the type to avoid excessβproperty errors and correctly type editor.changeSettings payload.
-let currentEditorConfig: EditorConfig;
+let currentEditorConfig: EditorConfig & ReturnType<typeof getFormatterConfig>;If EditorConfig already includes those formatter props in ./models, ignore this and confirm.
π Script executed:
#!/bin/bash
set -e
# locate EditorConfig declarations
rg -nP "export\\s+(?:interface|type)\\s+EditorConfig" -g "*.ts" -n
rg -nP "interface\\s+EditorConfig\\b" -g "*.ts" -n
# locate getFormatterConfig definition
rg -nP "export\\s+function\\s+getFormatterConfig" -g "*.ts" -n
rg -nP "getFormatterConfig\\s*=" -g "*.ts" -nLength of output: 107
π Script executed:
#!/bin/bash
set -e
# Show full EditorConfig definition
sed -n '700,760p' src/sdk/models.ts
# Search for getFormatterConfig references and definition
rg -nC3 'getFormatterConfig' -g '*.ts'
# Broader search for any formatter-related config functions
rg -nC3 'formatterConfig' -g '*.ts'Length of output: 17497
Widen currentEditorConfig type to include formatter settings
Change its declaration in src/livecodes/core.ts (around lines 213β214) to include the fields returned by getFormatterConfig and avoid excess-property errors:
-let currentEditorConfig: EditorConfig;
+let currentEditorConfig: EditorConfig & ReturnType<typeof getFormatterConfig>;π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| let currentEditorConfig: EditorConfig; | |
| let toolsPane: ToolsPane | undefined; | |
| let currentEditorConfig: EditorConfig & ReturnType<typeof getFormatterConfig>; | |
| let toolsPane: ToolsPane | undefined; |
π€ Prompt for AI Agents
In src/livecodes/core.ts around lines 213β214, the declared variable
currentEditorConfig is too narrow and causes excess-property errors for the
formatter settings returned by getFormatterConfig; update its type to include
the formatter fields (for example by intersecting EditorConfig with the return
type of getFormatterConfig or by extending EditorConfig to include those
properties) so that currentEditorConfig accepts the formatter configuration
shape returned by getFormatterConfig; ensure any necessary imports/types are
adjusted accordingly.
| const content = arr[5]; | ||
| if (content?.trim()) { | ||
| css += `\n${content}`; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skip non-CSS <style> blocks before appending.
We now append every <style> blockβs contents to the Tailwind CSS input, even when the block declares lang="scss"/lang="less" (or any non-CSS preprocessor). Those blocks previously had no effect; after this change they inject raw SCSS/LESS tokens into the Tailwind/PostCSS pipeline and immediately throw (e.g., $color: red; causes βUnexpected token $β), breaking existing projects that rely on preprocessed styles. Please gate on the lang attribute and only merge blocks that are plain CSS/PostCSS.
for (const arr of [...html.matchAll(new RegExp(pattern, 'g'))]) {
- const content = arr[5];
- if (content?.trim()) {
- css += `\n${content}`;
- }
+ const lang = arr[3]?.trim().toLowerCase();
+ if (lang && !['css', 'postcss', 'tailwindcss'].includes(lang)) {
+ continue;
+ }
+ const content = arr[5];
+ if (content?.trim()) {
+ css += `\n${content}`;
+ }
}π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const content = arr[5]; | |
| if (content?.trim()) { | |
| css += `\n${content}`; | |
| } | |
| for (const arr of [...html.matchAll(new RegExp(pattern, 'g'))]) { | |
| const lang = arr[3]?.trim().toLowerCase(); | |
| if (lang && !['css', 'postcss', 'tailwindcss'].includes(lang)) { | |
| continue; | |
| } | |
| const content = arr[5]; | |
| if (content?.trim()) { | |
| css += `\n${content}`; | |
| } | |
| } |
| name: 'cpp-wasm', | ||
| aliases: ['clang'], | ||
| title: 'C++ (Wasm) Starter', | ||
| title: window.deps.translateString('templates.starter.cpp-wasm', 'C++ (Wasm) Starter'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard window access so builds/tests without a DOM donβt crash
At module load time this now dereferences window, which will throw in any Node/SSR/test context that imports the starter metadata (those paths previously worked because the string was static). Please keep the translation lookup lazy by first checking typeof window !== 'undefined' (or deferring the call) so build scripts and unit tests can still require this module safely.
- title: window.deps.translateString('templates.starter.cpp-wasm', 'C++ (Wasm) Starter'),
+ title:
+ typeof window !== 'undefined'
+ ? window.deps.translateString('templates.starter.cpp-wasm', 'C++ (Wasm) Starter')
+ : 'C++ (Wasm) Starter',π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| title: window.deps.translateString('templates.starter.cpp-wasm', 'C++ (Wasm) Starter'), | |
| // before: | |
| - title: window.deps.translateString('templates.starter.cpp-wasm', 'C++ (Wasm) Starter'), | |
| // after: | |
| title: | |
| typeof window !== 'undefined' | |
| ? window.deps.translateString('templates.starter.cpp-wasm', 'C++ (Wasm) Starter') | |
| : 'C++ (Wasm) Starter', |
π€ Prompt for AI Agents
In src/livecodes/templates/starter/cpp-wasm-starter.ts around line 6 the module
currently dereferences window at import time (window.deps.translateString(...)),
which throws in Node/SSR/test environments; change this to perform the
translation lazily by first checking typeof window !== 'undefined' before
calling window.deps.translateString, or defer the call behind a function/getter
so imports use a safe fallback (e.g., the raw English string) when window is
absent and only call translateString at runtime when a DOM/window is present.
| 'go', | ||
| 'go-wasm', | ||
| 'php', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion | π Major
π§© Analysis chain
Command menu: added βgo-wasmβ template β LGTM.
Matches the starter registry; discoverability ensured.
π Script executed:
#!/bin/bash
# Crossβcheck that the command menu list matches starterTemplates registry.
rg -nP 'starterTemplates\s*=\s*\[' -n src/livecodes/templates/starter/index.ts -C3
rg -nP "stringUnionToArray<TemplateName>\\(\\)\\(" -n src/livecodes/UI/command-menu-actions.ts -C5Length of output: 605
π Script executed:
#!/bin/bash
# List all starter template files
ls src/livecodes/templates/starter
# Show imports and array entries in starter index
sed -n '1,200p' src/livecodes/templates/starter/index.ts
# Show the children list in the command menu actions
sed -n '250,330p' src/livecodes/UI/command-menu-actions.tsLength of output: 7707
Align command menu entries with starter registry
The children array in src/livecodes/UI/command-menu-actions.ts includes templates (pyodide, clang) that arenβt present in starterTemplates (and will break discoverability). Remove or rename those entries so the command menu exactly mirrors src/livecodes/templates/starter/index.ts.
π€ Prompt for AI Agents
In src/livecodes/UI/command-menu-actions.ts around lines 300 to 302, the
children array contains template keys (e.g., 'pyodide', 'clang') that do not
exist in src/livecodes/templates/starter/index.ts and will break
discoverability; update the children array to exactly match the keys exported by
starterTemplates in src/livecodes/templates/starter/index.ts by removing or
renaming any entries not present in that file and ensure order/membership mirror
starterTemplates so the command menu and starter registry stay in sync.
| const srcObjProp = srcObj[key]; | ||
| const dstObjProp = dstObj[key]; | ||
| if (typeof srcObjProp === 'function') { | ||
| continue; | ||
| } else if (!(key in dstObj)) { | ||
| diff.push(key); | ||
| } else if (srcObj[key] !== null && typeof srcObj[key] === 'object') { | ||
| const objDiff = compareObjects(srcObj[key] as any, dstObj[key] as any).map( | ||
| (k) => `${key}.${k}`, | ||
| ); | ||
| diff.push(...objDiff); | ||
| } else if (srcObj[key] !== dstObj[key]) { | ||
| } else if (srcObjProp !== null && typeof srcObjProp === 'object') { | ||
| if (!dstObjProp || typeof dstObjProp !== 'object') { | ||
| diff.push(key); | ||
| } else if (Array.isArray(srcObjProp)) { | ||
| if (!Array.isArray(dstObjProp)) { | ||
| diff.push(key); | ||
| } else if (srcObjProp.length !== dstObjProp.length) { | ||
| diff.push(key); | ||
| } else { | ||
| for (let i = 0; i < srcObjProp.length; i++) { | ||
| if (srcObjProp[i] !== dstObjProp[i]) { | ||
| diff.push(`${key}[${i}]`); | ||
| } | ||
| } | ||
| } | ||
| } else { | ||
| const objDiff = compareObjects(srcObjProp, dstObjProp).map((k) => `${key}.${k}`); | ||
| diff.push(...objDiff); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Restore type compatibility when recursing
Inside the object branch, srcObjProp/dstObjProp are still typed as unknown/object, so the recursive call fails TS compilation (object isnβt assignable to Partial<Record<string, unknown>>). Please coerce the values before recursing.
- } else {
- const objDiff = compareObjects(srcObjProp, dstObjProp).map((k) => `${key}.${k}`);
+ } else {
+ const objDiff = compareObjects(
+ srcObjProp as Partial<Record<string, unknown>>,
+ dstObjProp as Partial<Record<string, unknown>>,
+ ).map((k) => `${key}.${k}`);π€ Prompt for AI Agents
In src/livecodes/utils/utils.ts around lines 631 to 655, the recursive call
compareObjects(srcObjProp, dstObjProp) fails TypeScript because
srcObjProp/dstObjProp are typed as object/unknown; coerce them to the expected
type before recursing (e.g. cast to Partial<Record<string, unknown>> or the
function's accepted param type) so the compiler accepts the call, then pass the
coerced vars into compareObjects and map the results as currently done.
| export const yaegiWasmBaseUrl = /* @__PURE__ */ getUrl('[email protected]/src/'); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π§© Analysis chain
New yaegiWasmBaseUrl β confirm required assets resolution.
If the Go (Wasm) runtime expects specific paths (e.g., yaegi.wasm, glue JS), ensure theyβre resolvable under LOCAL_MODULES and included by scripts/download-modules.js.
π Script executed:
#!/bin/bash
# Verify consumers and expected filenames for Yaegi.
rg -nP --type=ts --type=tsx --type=js 'yaegiWasm(Base)?Url|yaegi\-wasm' -C3Length of output: 27
π Script executed:
#!/bin/bash
# Search for yaegi URL constants and module references in codebase
rg -n 'yaegiWasmBaseUrl|yaegi-wasm' -C3 .
# Locate download-modules.js and inspect for yaegi asset inclusion
download_script=$(find . -type f -name download-modules.js)
if [ -n "$download_script" ]; then
echo "Inspecting $download_script"
rg -n 'yaegi' -C3 "$download_script"
else
echo "download-modules.js not found"
fiLength of output: 2372
Add Yaegi assets to download script: The Go Wasm worker imports ${yaegiWasmBaseUrl}wasm_exec.js and yaegi-browser.wasm; update scripts/download-modules.js to fetch these into LOCAL_MODULES.
π€ Prompt for AI Agents
In src/livecodes/vendors.ts around lines 110-111 we expose yaegiWasmBaseUrl; the
download script currently doesn't fetch Yaegi assets referenced by the Go Wasm
worker. Update scripts/download-modules.js to add two entries to the
LOCAL_MODULES list: `${yaegiWasmBaseUrl}wasm_exec.js` and
`${yaegiWasmBaseUrl}yaegi-browser.wasm` (or construct the same base URL via
getUrl('[email protected]/src/') in that script), so the download step fetches
both files into the local modules folder and preserves filenames/paths expected
by the worker.



What type of PR is this? (check all applicable)
Description
This PR continues on the previous PR (#857 - that added self-hosting using docker).
It allows running the app in docker while offline, with no internet connection.
LiveCodes depends on a large number of external modules to support a wide range of features (e.g. code editors, compilers, formatters, etc). These modules are loaded from CDNs (e.g. jsDelivr, unpkg, etc). So, in spite of being a client-side app, LiveCodes requires an internet connection to load these modules.
This PR allows LiveCodes to run without an internet connection. This can be achieved by setting the environment variable
LOCAL_MODULES=true(it is set tofalseby default). In this case, all modules are downloaded during build (~ 1.5 GB), and are served locally.The downloaded modules are cached in a docker layer and only re-downloaded when modules (in vendors.ts) change. This way, the code stays the source of truth.
See docs: https://local-modules.livecodes.pages.dev/docs/advanced/docker#local-modules
Related Tickets & Documents
#857
Closes #807
Mobile & Desktop Screenshots/Recordings
livecodes-local-modules.mp4
livecodes-offline-custom-modules.mp4
Added tests?
Added to documentations?
I would appreciate it if you could have a look whenever suits you
@mtantawy @zyf722 @Seth0x41 @aabouzaid @BassemHalim @mrgb7 @sharno
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Documentation
Chores