Repository navigation
Eleven new tools: YAML/TOML, SQL, CSS, CSV, cron, QR and more - #23
Merged
Merged
Conversation
Rewrites the 55 classes the Tailwind editor extension flagged as having a canonical spelling (w-[220px] → w-55, rounded-[2px] → rounded-xs, bg-[length:…] → bg-size-[…], [scrollbar-width:none] → scrollbar-none, [&>*]: → *:, and so on). Computed styles are identical before and after on 14 routes at 1280px and 400px. scripts/tailwind.ts runs Tailwind's own canonicalizer (the engine behind the editor hints) over every string and template literal in src/**/*.tsx, parsed with oxc so JSX text is never touched. `pnpm tw:fix` reports them and `--write` rewrites them; scripts/tailwind.test.ts fails CI when one appears, so new code, including agent-written tools, stays canonical. Adds @tailwindcss/node 4.3.3 as a dev dependency for its design-system loader.
Map.groupBy is ES2024 and the repo targets the ES2023 lib, so the script failed typecheck.
The test generated two ULIDs with the same seed but the real clock, so a millisecond tick between the calls changed the time prefix and lower !== upper.toLowerCase(). Both calls now use the fixed clock the rest of the file uses.
Registers escape, env, color, contrast, lorem, cron, csv, yaml-toml, qr, sql and css with placeholder shells and placeholder devtools-core subpaths, so each tool can be filled in without touching shared files. All new dependencies are added here, so no tool needs to touch package.json or the lockfile: yaml, smol-toml, cronstrue and uqr as core dependencies; prettier and sql-formatter as optional core peers (and app dependencies), so npm consumers of other subpaths don't install them. Each was checked for eval / new Function, which the CSP forbids.
Adds the @fadeltd/devtools-core/env engine and the tool. parseDotenv reads .env files the way dotenv does: export prefix, spaces around =, full-line and inline comments, literal single quotes and backticks, double quotes with \n \r \t \" \\ escapes and multiline values, CRLF and BOM, duplicate keys (last wins, warned with both line numbers), and positioned errors for bad keys, missing =, unclosed quotes and text after a closing quote. An inline # needs whitespace before it; a glued # is kept and warned about, because Node's dotenv would silently truncate that value. parseJsonEnv uses a small line-tracking JSON parser so every key keeps its line, number literals keep their exact text, and repeated keys are visible. Nested values are errors unless flatten (__ and array indexes) is on; null becomes an empty value with a warning. Serializers: JSON (strings, entry order); .env with the lightest portable quoting (bare, double quotes without escapes, single quotes for ", \ or $, double quotes with escapes only when unavoidable); shell export with POSIX single-quote escaping, reporting keys that are not shell identifiers; docker --env-file with raw values, reporting multiline values. Property tests cover .env → JSON → .env and shell output re-read by parseShellExports. The UI has input (auto/.env/JSON) and output selects, a flatten toggle for JSON input, a Mask values toggle that masks the preview while Copy copies the real output, and issues listed under the output by line. store: none, so nothing is ever saved.
Core `qr` builds the payload (plain text; a URL checked by the platform parser, with https:// added to a bare host and warnings for non-http(s) schemes; a Wi-Fi `WIFI:` string with `\ ; , : "` escaped and WPA/WEP/open security), encodes it with uqr at a fixed ECC level and quiet zone, and returns the module matrix with its version, size, mode and byte count. Too-long input is reported as data with the byte count and the version 40 limit for that mode and ECC level, checked before uqr runs so a pasted megabyte fails fast; the capacity table is pinned against uqr in the tests. Lone surrogates are refused instead of being silently replaced. The SVG download is a single run-length-merged path plus a background rect, with colours validated as #rrggbb before they reach markup. The same path string drives the live preview as a React `d` prop, so no SVG string ever enters the DOM. The PNG is drawn to a canvas in the shell at a whole number of pixels per module, so edges stay sharp. The tool persists everything except the Wi-Fi password and its show/hide state, via the `redact` option, and the encoded-text readout masks the password while the field is hidden. Colours warn below 4:1 contrast and when inverted, and the code warns on a quiet zone under 2 modules and on dense versions.
…nd HTML (B-12)
Adds the escape engine to devtools-core and the tool shell. Six modes, both directions: JSON strings (exactly JSON.stringify, with an ASCII-only option; strict unescaping that accepts exactly what JSON.parse accepts), JavaScript strings for any quote style (escapes U+2028/2029, lone surrogates and ${ in templates; unescaping follows strict-mode rules, including \u{…} and line continuations), SQL literals (standard '' doubling, plus a MySQL backslash variant), POSIX sh single-quoting with the '\'' idiom, regex literals, and HTML entities (reusing html-text's entity table).
Bad input comes back as data: every unescape error names the problem, its position (line and column past the first line) and the fix. Warnings cover what escapes correctly but may still surprise, such as a backslash in a standard SQL literal that MySQL would read as an escape, or NUL in a shell argument.
Property tests round-trip every mode, check JSON.parse(escapeJson(x)) === x, compare JS literals against a real engine via node:vm, assert shell output is only single-quoted runs joined by \', and check escaped regexes match exactly the input under no flags, u and v.
… Markdown (B-22) Adds the lorem engine to devtools-core and the tool shell. It generates an exact number of words, sentences or paragraphs from the classic vocabulary, optionally opening with "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", and renders it as plain text, HTML <p> lines or Markdown with the odd emphasised word. The engine takes a RandomSource, so tests pin a seed. The tool draws a crypto-random seed and Regenerate (⌘⏎) replaces it, so changing the format re-renders the same words. Counts are capped at 50,000 words, 5,000 sentences or 1,000 paragraphs, with a note when the cap applies. Only the options are stored; the text is regenerated on every visit.
Adds the color engine to devtools-core: a CSS color parser (hex in all four lengths, rgb()/rgba() and hsl()/hsla() in comma and space syntax with / alpha, percentages, none and angle units, oklch(), and all 148 named colors plus transparent) that reports bad input with a position, and conversion between sRGB, HSL and OKLCH through Björn Ottosson's OKLab matrices. An OKLCH color outside sRGB is kept as written and flagged; hex, rgb() and hsl() show its chroma-reduced version, with lightness and hue unchanged. Values a browser would clamp are clamped and noted. Outputs are canonical CSS, and the printed oklch() of any hex reads back in gamut and within one step per channel. The tool has one input with a native picker and a large swatch (split into as-written and in-sRGB halves when out of gamut, checkerboarded when translucent), and copyable rows for hex, rgb(), hsl(), oklch(), alpha and the CSS name.
Adds WCAG 2.x relative luminance and contrast ratio to the color engine, with verdicts for AA and AAA at normal and large text sizes and the 3:1 non-text threshold. A translucent foreground is blended over the background before measuring and a translucent background over white, and each assumption is stated. The ratio prints rounded down, so a failing 4.499 never shows as 4.50. suggestForeground finds the nearest foreground that meets a target by moving OKLCH lightness alone in both directions, judging each candidate on its rounded 8-bit hex so the suggestion passes as pasted. It returns null only when even black and white fall short. The tool takes foreground and background with pickers and a swap button, previews sample text at 24px, 16px and 13px plus a bordered control in the chosen colors, shows the ratio large beside a pass/fail verdict table, and offers suggested AA and AAA foregrounds with Copy and Apply.
Adds the cron engine to @fadeltd/devtools-core/cron: a hand-written parser for 5-field cron and 6-field with seconds first (lists, ranges, steps, month and weekday names in any case, day-of-week 7 as Sunday, the @ macros, @reboot as "no schedule"), with every error returned as data carrying its field and character position. Quartz-only syntax (?, L, W, #, a seventh year field) is refused by name, and a pasted crontab line with a command on the end is recognised as such. Next runs walk wall-clock time in the chosen IANA zone using the timestamp zone helpers. Day-of-month and day-of-week follow Vixie: both restricted means either matches, and a field starting with * (including */2) counts as unrestricted. Daylight saving follows Vixie too: a fixed-time run in a spring-forward gap fires at the first instant after it, and once at the first occurrence in a fall-back overlap; schedules with * in the minute or hour field skip the gap and run through both passes. Impossible dates such as 0 0 30 2 * are reported as never firing without searching, and the search is capped at 50 years without a run. The English sentence comes from cronstrue, given the parsed expression, with a 24-hour option; a cronstrue throw becomes an error object. The tool shows the description large, a per-field breakdown, the next 10 runs with zoned ISO, relative time and copy, quick-pick examples, and inline errors with a caret. Expression, zone and 24-hour choice persist.
Adds a hand-written, linear-time RFC 4180 parser to devtools-core/csv: quoted fields with "" escapes and embedded delimiters and newlines, CRLF/LF/lone-CR endings, BOM stripped and reported, no phantom row from a trailing newline. An unterminated quote is reported at its position and read as a literal quote so it cannot swallow the rest of the file; text after a closing quote is kept and reported. Ragged rows are reported with expected vs got and padded, and extra cells widen the table instead of being dropped. Delimiter sniffing picks comma, tab, semicolon or pipe by field-count consistency over the first 50 records, read quote-aware, so European CSV with decimal commas resolves to semicolon. Outputs: JSON (objects written by hand so integer-like header keys keep column order; empty and duplicate names renamed), GitHub Markdown with escaped pipes and right-aligned number columns, and CSV/TSV with minimal quoting. Per-column type hints (number, boolean, date, text) come from the first 1,000 rows. The tool parses in a worker via useWorkerJob and returns the table packed as one string plus a Uint32Array of offsets, so a 10 MB paste does not cost a million-string structured clone. The grid is virtualised in both directions with a sticky header, a visible type-hint row and sticky row numbers; ragged rows are marked, and each warning selects its position in the input.
Adds convert(input, from, to, options) to @fadeltd/devtools-core/yaml-toml, with auto-detection (JSON, then TOML, then YAML, falling back to the format the first line looks like so a broken TOML or JSON file is not read as a YAML string). Every parse error comes back as data with line, column and a caret snippet: JSON through the JSON tool's analyzer (JSONC accepted with a note), TOML from TomlError's position, YAML from each error's offset. YAML follows the 1.2 core schema unless a document declares %YAML 1.1. Multi-document streams become a JSON array or one picked document, and need a pick for TOML. Anchors, aliases and merge keys are resolved with a note, alias bombs are refused, duplicate keys are positioned errors, and custom tags are errors outside YAML (kept, never evaluated, YAML to YAML). The Norway problem, leading-zero octals and base-60 numbers are noted, and YAML output quotes such strings so 1.1 readers agree. YAML to YAML keeps comments. Integers beyond 2^53 stay exact end to end (written as digits in JSON, with a warning) without relying on JSON.rawJSON. TOML datetimes become ISO strings with a note, null is refused with its key path and position, and integers and floats stay distinct through YAML and TOML. The tool offers From and To selects, a swap button that moves the output into the input, an indent option per target, a document picker for YAML streams, and lists warnings and notes under the output.
Formats SQL with sql-formatter in twelve dialects (standard SQL, PostgreSQL, MySQL, MariaDB, SQLite, BigQuery, Snowflake, T-SQL, PL/SQL, Redshift, Spark, Trino), with keyword case, indent and blank lines between statements. Comments are kept. Parse errors come back as data with the line and column sql-formatter puts in its message, trimmed of the nearley grammar dump, plus the library's hint to pick a dialect when standard SQL cannot lex the input. Minify is a small per-dialect tokenizer in core: whitespace inside strings, quoted identifiers ([] in T-SQL, backticks), dollar-quoted bodies and comments is untouched, backslash escapes apply only where the dialect has them, and a kept -- comment keeps its newline so it cannot swallow the rest of the query. Strip comments keeps optimizer hints and MySQL versioned comments. sql-formatter is imported inside formatSql, and the tool runs it in a worker via useWorkerJob, since nearley can take seconds on a large dump; the worker can be cancelled after 2s. The library lands only in the worker chunk.
Formats CSS, SCSS and Less with prettier/standalone and its postcss plugin, both imported inside formatCss so nothing loads them until a stylesheet is formatted. Options: print width, indent (2, 4 or tabs) and single quotes. Prettier syntax errors come back as data with loc.start line and column, without the code frame. Minify is hand-written in core for plain CSS: comments go except /*! */, whitespace collapses outside strings, escapes and unquoted url() bodies, the last semicolon in each block and empty declarations go, and spaces stay where they carry meaning: around + and - inside parentheses (calc), before a colon (a :hover), before ( (and (min-width)), and after a hex escape. Minifying SCSS or Less says it is CSS-only and offers to switch the parser. Formatting runs in a worker via useWorkerJob, since Prettier is synchronous under its async API; the first format shows Loading formatter, and a slow run can be cancelled. Prettier lands only in the worker chunk.
fast-check found that `*!` made convert() throw from the yaml library's toString: the parser accepts an alias whose anchor never appears, and the library only fails later when writing. The reader now reports it as a positioned error for every target. The JSON/TOML path had mislabelled the same failure as "Too many aliases"; that message is now kept for the real alias-count guard. convert() also gets a backstop catch so its never-throws contract doesn't depend on catching every library edge case individually; 50,000 random inputs no longer reach it.
Documents the eleven batch-2 tools in both READMEs and both changelogs (site and devtools-core minor releases via the Added headings), lists the new core subpaths with the optional sql-formatter and prettier peers, and adds every tool to the bug report dropdown.
# Conflicts: # packages/core/package.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Added
@fadeltd/devtools-coresubpaths for each engineFixed