Popularne pakiety
Zgodność ze specyfikacją nie gwarantuje działania rzeczywistych bibliotek. Silniki JS, które przechodzą 99 % testów test262, wciąż regularnie zawodzą w kodzie produkcyjnym, ponieważ biblioteki zależą od wielu rzadkich „szczegółów specyfikacji” — pułapek Proxy w określonej kolejności, kształtu Function.prototype.toString, zachowania wyrażeń regularnych przy trudnych danych wejściowych oraz dokładnego wpływu kolejności Object.keys na nazwy obliczanych właściwości.
Phasis weryfikuje 204 szeroko używanych bibliotek bajt po bajcie względem Node.js (V8). Znajdują się one w repozytorium pod tests/Popular/, a CI uruchamia testy przy każdej zmianie kodu źródłowego silnika lub zestawu testów.
Zakres według kategorii
| Kategoria | Biblioteki |
|---|---|
| Parsery / AST | acorn 8.16.0, chevrotain 11.0.3, parse5 7.2.1, parsimmon 1.18.1, jsep 1.4.0, postcss-value-parser 4.2.0, acorn-walk 8.3.4, estraverse 5.3.0, escodegen 2.1.0, css-tree 3.0.4, sax 1.4.1, nearley 2.20.1 |
| Szablony / Markdown / HTML | mustache 4.2.0, handlebars 4.7.9, marked 18.0.3, markdown-it 14.1.0 (+ markdown-it-emoji), markdown-table 3.0.4, showdown 2.1.0, cheerio 1.1.2, htmlparser2 11.0.0 |
| Narzędzia / funkcyjne | lodash 4.18.1, ramda 0.31.3, remeda 2.17.4, es-toolkit 1.27.0, immutable 5.1.3, immer 10.1.1, redux 5.0.1, deepmerge 4.3.1, dot-object 2.1.5, lodash.merge 4.6.2, lodash.clonedeep 4.5.0, extend 3.0.2, extend-shallow 3.0.2, mixin-deep 2.0.1, merge-options 3.0.4 |
| Dostęp do obiektów | dequal 2.0.3, clsx 2.1.1, classnames 2.5.1, get-value 4.0.1, set-value 4.1.0, object.omit 3.0.0, object.pick 1.3.0, is-plain-obj 4.1.0, kind-of 6.0.3 |
| Walidacja / schematy | zod 4.4.3, yup 1.4.0, valibot 1.1.0, superstruct 2.0.2, validator 13.12.0, email-validator 2.0.4, ajv 8.17.1, @sinclair/typebox 0.34.42, runtypes 6.7.0, typanion 3.14.0, ow 2.0.0 |
| Daty / godziny | dayjs 1.11.20, date-fns 4.1.0, timeago.js 4.0.2, luxon 3.7.3, moment 2.30.2 |
| Matematyka / liczby | bignumber.js 11.1.1, decimal.js 10.6.0, numeral 2.0.6, jsbi 4.3.0, big.js 6.2.2, fraction.js 4.3.7, complex.js 2.4.2, simple-statistics 7.8.8 |
| Skróty / kryptografia | blueimp-md5 2.19.0, crypto-js 4.2.0, fnv-plus 1.3.1, murmurhash3js 3.0.1, hash-sum 2.0.0, jwt-decode 4.0.0, @noble/hashes 2.2.0, tweetnacl 1.0.3, aes-js 3.1.2, bcryptjs 3.0.2, xxhashjs 0.2.2, object-hash 3.0.0 |
| Wyszukiwanie / różnice | fuse.js 7.3.0, minisearch 7.1.1, diff 8.0.2, diff-match-patch 1.0.5, fast-deep-equal 3.1.3, fast-equals 5.2.2, fastest-levenshtein 1.0.16, lunr 2.3.9 |
| JSON / formaty danych | json5 2.2.3, js-yaml 4.1.0, yaml 2.6.1, toml 3.0.0, smol-toml 1.3.1, fast-json-patch 3.1.1, jmespath 0.16.0, jsonata 2.0.6, jsonpath-plus 10.3.0, mingo 6.7.0, sift 17.1.3, fast-json-stable-stringify 2.1.0, safe-stable-stringify 2.5.0, json-stringify-safe 5.0.1, canonicalize 2.1.0, papaparse 5.5.4 |
| Ciągi / językoznawstwo | he 1.2.0, slugify 1.6.9, camelcase 8.0.0, entities 6.0.1, pluralize 8.0.0, inflection 3.0.2, wcwidth 1.0.1, emoji-regex 10.4.0, unraw 3.0.0, jsesc 3.1.0, lower-case 2.0.2, upper-case 2.0.2, dot-case 3.0.4, pascal-case 3.1.2, snake-case 3.0.4, title-case 3.0.3, xregexp 5.1.1, escape-string-regexp 5.0.0, ansi-regex 6.1.0, strip-ansi 7.1.2, string-width 8.1.0 |
| URL / internet | url-parse 1.5.10, qs 6.13.1, query-string 9.1.1, tldts 6.1.69, punycode 2.3.1, linkifyjs 4.3.1, ipaddr.js 2.2.0, uri-js 4.4.1, url-template 3.1.1, bowser 2.11.0 |
| Kompresja / kodowanie / archiwa | pako 2.1.0, lz-string 1.5.0, base64-js 1.5.1, JSZip 3.10.1 |
| Kolory | color-string 2.1.1, d3-color 3.1.0, chroma-js 3.1.2, color-name 2.0.0, tinycolor2 1.6.0, color 5.0.3 |
| Klonowanie / równość | rfdc 1.4.1, klona 2.0.6, fast-copy 3.0.2 |
| Formatowanie / wyświetlanie | ms 2.1.3, bytes 3.1.2 |
| Identyfikatory / UUID | uuid 10.0.0, hexoid 2.0.0, ulid 3.0.1, cuid2 2.2.2, short-unique-id 5.4.3 |
| Rozpoznawanie typów / glob | type-detect 4.1.0, picomatch 4.0.2, minimatch 10.1.1, wildcard-match 5.1.4, glob-to-regexp 0.4.1, to-regex 4.0.4, array-uniq 3.0.0, array-unique 0.3.2 |
| Semver / konfiguracja | semver 7.6.3, compare-versions 6.1.1, semver-compare 1.0.0 |
| Pamięć podręczna / wydajność | lru-cache 11.0.2, marky 1.3.0, array-flatten 3.0.0, arr-diff 4.0.0 |
| Asynchroniczność / zdarzenia | eventemitter3 5.0.1, eventemitter2 6.4.10, mitt 3.0.1, p-limit 7.1.1, p-queue 9.0.2, p-defer 5.0.0, p-retry 7.1.0, throttle-debounce 5.0.2, async 3.2.6 |
| Stan reaktywny | mobx 6.13.7, xstate 5.23.0, valtio 2.1.5, nanostores 1.0.1, jotai 2.16.1, redux-saga 1.3.0, redux 5.0.1 |
| CSS / stylizacja | postcss 8.5.7, stylis 4.3.6, css-tree 3.0.4 |
| Budowanie / AST / kompilatory | magic-string 0.30.20, escodegen 2.1.0, acorn-walk 8.3.4, estraverse 5.3.0, @babel/parser 7.28.5, terser 5.46.0, prettier 3.3.3, svelte 5.55.7 (kompilator), typescript 6.0.3 |
| MQTT / protokół | mqtt-packet 9.0.4 |
| GraphQL | graphql-js 16.11.0 |
| Podświetlanie składni | highlight.js 11.11.1 |
| Testowanie / asercje | chai 6.2.1 |
Łącznie: 204 bibliotek dokładnie odtwarza wynik Node. Wszystkie są przypięte do najnowszego wydania upstream z chwili ostatniej aktualizacji.
Jak to działa
Każdy scenariusz to katalog tests/Popular/<lib>/ zawierający trzy pliki:
tests/Popular/lodash/
├── lodash.js ← vendored upstream library (untouched, UMD bundle)
├── runner.js ← Phasis-side script that exercises the library
└── oracle.txt ← Node.js output of running runner.js (committed)
tests/Popular/PopularPackagesTest.php to test z dostawcą danych PHPUnit, który:
- Ładuje
lodash.js+runner.jsdo nowegonew Phasis\Engine(). - Zapisuje wyjście
console.logw buforze. - Sprawdza, czy przechwycony bufor jest bajtowo identyczny z
oracle.txt.
Ponownie wygeneruj wzorzec za pomocą:
node tests/Popular/gen-oracle.js tests/Popular/<lib>
(Używane po aktualizacji dołączonej biblioteki — ponownie uruchamia program testowy w rzeczywistym Node i nadpisuje oracle.txt.)
Kontrola CI
Workflow: .github/workflows/popular.yml.
Uruchamia się przy wypchnięciu zmian i żądaniach pull do main, jeśli zmieniono src/, tests/Popular/, phpunit.xml.dist, pliki Composer lub sam workflow, a także przy ręcznym uruchomieniu. Dwa kroki:
- Strona Phasis — uruchamia zestaw testów PHPUnit
popular, który wykonuje każdą bibliotekę przez Phasis i porównuje wynik z zatwierdzonym wzorcem. Kończy się błędem, jeśli różni się choćby jeden bajt. - Ponowna weryfikacja w Node — uruchamia
node tests/Popular/gen-oracle.js …dla każdej biblioteki i sprawdza, czy świeżo wygenerowany wzorzec zgadza się z tym zapisanym w repozytorium. Wykrywa rozbieżności typu „zaktualizowano dołączoną bibliotekę bez aktualizacji wzorca”.
Oba kroki muszą się powieść, zanim PR zostanie scalony. Zmiana w test262, która psuje lodash, jest nie do przyjęcia; podobnie zmiana w lodash, która psuje test262.
Dlaczego te biblioteki
- acorn — parsowanie to najbardziej podatny na błędy obszar każdego silnika JS. Sprawdza każdy przypadek brzegowy składni dodany przez współczesny JS. Jeśli Phasis potrafi analizować dowolny nowoczesny kod za pomocą acorn, środowisko wykonawcze działa poprawnie.
- mustache.js — obsługa szablonów to jedno z zastosowań osadzania, które Phasis wyraźnie wspiera (pierwszym konkretnym przykładem w README są „silniki szablonów”). Mustache sprawdza iterację, rozwiązywanie zakresów i przekształcanie ciągów w kolejności stosowanej w rzeczywistych szablonach.
- lodash — najczęściej używana biblioteka narzędziowa JS na świecie. Jej ponad 300 funkcji pośrednio wykorzystuje każdą metodę biblioteki standardowej. Jeśli lodash działa, działają też ogólne JS do przekształcania danych.
- marked — renderowanie markdown. Praktyczny test, który sprawdza wyrażenia regularne (w dużej liczbie) i manipulowanie ciągami ze stanem między wierszami. Ujawnia rozbieżności między silnikiem wyrażeń regularnych PCRE2 (awaryjnym dla Phasis) a Irregexp używanym przez V8.
- dayjs — działania na datach i godzinach często powodują błędy o jeden dzień, których nie wykrywa żaden przypadek test262. dayjs sprawdza różne warianty konstruktora, działania na granicach miesięcy odpowiadających zmianom czasu letniego oraz rozwijanie tokenów formatu w zbliżonych ścieżkach kodu, z których korzysta każda biblioteka dat JS.
- bignumber.js — arytmetyka dowolnej precyzji bez TypedArrays sprawdza parsowanie liczb z ciągów, pętle mnożenia i dzielenia dużych liczb całkowitych oraz wybór trybu zaokrąglania — wszystko w czystym JS, więc cały koszt ponosi silnik. Ujawnia subtelne problemy z konwersją Number ↔ String.
- json5 — liberalne parsowanie JSON w dużej mierze opiera się na wyrażeniach regularnych z klasami znaków i tokenizacji zachowującej stan. To dobry test zarówno poprawności wyrażeń regularnych, jak i granicznych przypadków iteracji po ciągach.
- fuse.js — UMD przetranspilowany przez Babel, który sprawdza
_classCallCheck(wzorzecthis instanceof Ctorstosowany przez każdą klasę transpilowaną sprzed ES6), a także pełny algorytm rozmytego dopasowywania bitap. Tę postać mają biblioteki transpilowane przez Babel, więc każda usterka w Fuse psuje połowę ekosystemu npm. - handlebars — zaawansowana obsługa szablonów z kompilacją przed renderowaniem. Sprawdza ścieżkę środowiska wykonawczego bez
evalprzez jeden ogromny wpiscompile(), przechodzenie po AST, wywoływanie helperów za pomocą dynamicznej tabeli wyszukiwania oraz sprawdzanie markiSafeStringprzy użyciuinstanceofmiędzy różnymi realmami. Stanowi kontrast dla prostszego mechanizmu przechodzenia po szablonach w mustache.js. - zod — walidacja schematów w czasie działania. Rozbudowane hierarchie klas (
ZodString,ZodObject,ZodArray,ZodUnion, …) z łańcuchami metod zwracającymi uszczegółowione podklasy, formatowanie ustrukturyzowanych błędów, w którym tablica.issuespo stronie JS musi być bajtowo zgodna z Node, oraz wzorce dyskryminatorów i tagów literałów. Sprawdza kod walidacji intensywnie wykorzystujący programowanie obiektowe, który stanowi warstwę dla połowy współczesnych aplikacji TypeScript. - typescript — rzeczywisty kompilator TypeScript działający na osadzonym silniku. Zminifikowany kompilator o rozmiarze 9 MB, który ponownie parsuje dowolny kod TS, przechodzi po AST i generuje JS — najcięższy możliwy ładunek czystego JS. Jeśli Phasis potrafi transpilować typy generyczne, async/await, wyliczenia i hierarchie klas, zachowując zgodność bajt po bajcie z
tsc, semantyka języka jest poprawna. - prettier — formatter JS narzucający określony styl. Formatowanie kodu JS z JS sprawia, że ta sama ścieżka sterowania, która formatuje kod, sprawdza każdy szczegół specyfikacji obsługiwany przez silnik — to wyjątkowo wymagające obciążenie.
- svelte/compiler — samodzielny kompilator Svelte. Parsuje kod źródłowy
.svelte, przechodzi po AST i generuje JS dla celów klienta i SSR. - terser / @babel/parser / postcss — minifikator JS, parser JS, narzędzie do przetwarzania końcowego CSS. Dziesiątki tysięcy wierszy zminifikowanego kodu ponownie parsującego dowolne dane wejściowe — to obszar, w którym pojawiają się rzadkie struktury AST.
- ajv — walidator schematów JSON, który kompiluje każde schematy JIT do niestandardowej funkcji walidującej za pomocą
new Function(...). Sprawdza ścieżkę konstruktora Function, z której korzysta większość bibliotek schematów i szablonów. - mobx — reaktywny stan oparty na proxy. Każdy odczyt obserwowalnej wartości przechodzi przez pułapkę Proxy
get, która rejestruje zależność; każdy zapis wywołuje reakcje. To test obciążeniowy infrastruktury Proxy, obserwowalnych map, tablic i zagnieżdżonych obiektów oraz harmonogramu autorun. - cheerio / htmlparser2 — jQuery po stronie serwera, oparte na szybkim parserze HTML. Parsowanie selektorów, przechodzenie po strukturze w stylu DOM, modyfikowanie atrybutów i serializacja HTML w jednym pakiecie.
- graphql-js / jsonata — implementacja referencyjna GraphQL i język zapytań JSON. Oba rozwiązania intensywnie wykorzystują schematy, walidację i rekurencyjne ewaluatory — typowy kod „warstwy danych w przeglądarce”.
- JSZip — asynchroniczne narzędzie do tworzenia archiwów ZIP. Przetwarza zadania według harmonogramu opartego na
setImmediatei generuje rzeczywiste dane binarne, sprawdzając zarówno warstwę timerów, jak i ścieżki intensywnie wykorzystująceUint8Array. - moment / luxon — działania na datach i godzinach. moment to mutowalny, ręcznie dostrojony UMD; luxon to nowoczesne, niemutowalne przepisanie. Razem obejmują pełen zakres formatowania, parsowania i obliczeń na miesiącach i latach odpowiadających różnym strefom czasowym.
- mqtt-packet — czysty JS-owy kodek MQTT 3.1.1 / 5.0. Pakowanie i dekodowanie na poziomie bajtów oraz klasy znaków wyrażeń regularnych dla znaków spoza alfabetu base64, które sprawdzają graniczne przypadki gramatyki Annex-B.
- xstate / valtio / nanostores / jotai / redux-saga — przekrój współczesnych rozwiązań do zarządzania stanem. Maszyny stanów, stan oparty na proxy, magazyny atomów, atomy pochodne i generatory uruchamiające efekty — każda biblioteka sprawdza inny model reaktywności.
- escodegen / acorn-walk / estraverse / magic-string — narzędzia AST. Generowanie kodu, przechodzenie po strukturze i niedestrukcyjna edycja źródła — podstawowe elementy transformatorów JS na JS (narzędzi budujących, codemodów i linterów).
- papaparse / lunr / yaml / sax / css-tree — czyste parsery i tokenizatory. CSV, indeks wyszukiwania pełnotekstowego, YAML 1.2, strumieniowe parsowanie XML w stylu SAX i AST CSS. Różne gramatyki, a każda dobrze sprawdza skanowanie znak po znaku i zachowanie wyrażeń regularnych.
Dodawanie kolejnych bibliotek
Umieść nowy katalog w tests/Popular/<name>/, zachowując strukturę trzech plików, a następnie:
node tests/Popular/gen-oracle.js tests/Popular/<name> # one-time, generates oracle.txt
git add tests/Popular/<name>
Przy następnym uruchomieniu zestaw PHPUnit automatycznie wykryje nowy katalog, a CI uwzględni go, ponieważ zmiana dotyczy tests/Popular/.
Warto dodać między innymi: chevrotain (kombinator parserów, sprawdza domknięcia i wyrażenia regularne), nanoid (granice kryptografii i losowości), date-fns (obsługę stref czasowych wykraczającą poza dayjs).