diff --git a/tools/modern-tests/apps/typescript/.gitignore b/tools/modern-tests/apps/typescript/.gitignore new file mode 100644 index 0000000000..c2658d7d1b --- /dev/null +++ b/tools/modern-tests/apps/typescript/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/tools/modern-tests/apps/typescript/.meteor/.gitignore b/tools/modern-tests/apps/typescript/.meteor/.gitignore new file mode 100644 index 0000000000..4083037423 --- /dev/null +++ b/tools/modern-tests/apps/typescript/.meteor/.gitignore @@ -0,0 +1 @@ +local diff --git a/tools/modern-tests/apps/typescript/.meteor/.id b/tools/modern-tests/apps/typescript/.meteor/.id new file mode 100644 index 0000000000..e9e2dc15a7 --- /dev/null +++ b/tools/modern-tests/apps/typescript/.meteor/.id @@ -0,0 +1,7 @@ +# This file contains a token that is unique to your project. +# Check it into your repository along with the rest of this directory. +# It can be used for purposes such as: +# - ensuring you don't accidentally deploy one app on top of another +# - providing package authors with aggregated statistics + +k0367lnvnclor.z6cmuyuro69u diff --git a/tools/modern-tests/apps/typescript/.meteor/packages b/tools/modern-tests/apps/typescript/.meteor/packages new file mode 100644 index 0000000000..993162a9f0 --- /dev/null +++ b/tools/modern-tests/apps/typescript/.meteor/packages @@ -0,0 +1,23 @@ +# Meteor packages used by this project, one per line. +# Check this file (and the other files in this directory) into your repository. +# +# 'meteor add' and 'meteor remove' will edit this file for you, +# but you can also edit it by hand. + +meteor-base # Packages every Meteor app needs to have +mobile-experience # Packages for a great mobile UX +mongo # The database Meteor supports right now +reactive-var # Reactive variable for tracker + +standard-minifier-css # CSS minifier run for production mode +standard-minifier-js # JS minifier run for production mode +es5-shim # ECMAScript 5 compatibility for older browsers +ecmascript # Enable ECMAScript2015+ syntax in app code +typescript # Enable TypeScript syntax in .ts and .tsx modules +shell-server # Server-side component of the `meteor shell` command +hot-module-replacement # Update client in development without reloading the page + + +static-html # Define static page content in .html files +react-meteor-data # React higher-order component for reactively tracking Meteor data +zodern:types # Pull in type declarations from other Meteor packages diff --git a/tools/modern-tests/apps/typescript/.meteor/platforms b/tools/modern-tests/apps/typescript/.meteor/platforms new file mode 100644 index 0000000000..efeba1b50c --- /dev/null +++ b/tools/modern-tests/apps/typescript/.meteor/platforms @@ -0,0 +1,2 @@ +server +browser diff --git a/tools/modern-tests/apps/typescript/.meteor/release b/tools/modern-tests/apps/typescript/.meteor/release new file mode 100644 index 0000000000..621e94f0ec --- /dev/null +++ b/tools/modern-tests/apps/typescript/.meteor/release @@ -0,0 +1 @@ +none diff --git a/tools/modern-tests/apps/typescript/.meteor/versions b/tools/modern-tests/apps/typescript/.meteor/versions new file mode 100644 index 0000000000..ee3f71c234 --- /dev/null +++ b/tools/modern-tests/apps/typescript/.meteor/versions @@ -0,0 +1,68 @@ +allow-deny@2.1.0 +autoupdate@2.0.1 +babel-compiler@7.12.1 +babel-runtime@1.5.2 +base64@1.0.13 +binary-heap@1.0.12 +boilerplate-generator@2.0.1 +caching-compiler@2.0.1 +callback-hook@1.6.1 +check@1.4.4 +core-runtime@1.0.0 +ddp@1.4.2 +ddp-client@3.1.1 +ddp-common@1.4.4 +ddp-server@3.1.2 +diff-sequence@1.1.3 +dynamic-import@0.7.4 +ecmascript@0.16.12 +ecmascript-runtime@0.8.3 +ecmascript-runtime-client@0.12.3 +ecmascript-runtime-server@0.11.1 +ejson@1.1.5 +es5-shim@4.8.1 +facts-base@1.0.2 +fetch@0.1.6 +geojson-utils@1.0.12 +hot-code-push@1.0.5 +hot-module-replacement@0.5.4 +id-map@1.2.0 +inter-process-messaging@0.1.2 +launch-screen@2.0.1 +logging@1.3.6 +meteor@2.1.1 +meteor-base@1.5.2 +minifier-css@2.0.1 +minifier-js@3.0.3 +minimongo@2.0.3 +mobile-experience@1.1.2 +mobile-status-bar@1.1.1 +modern-browsers@0.2.3 +modules@0.20.3 +modules-runtime@0.13.2 +modules-runtime-hot@0.14.3 +mongo@2.1.3 +mongo-decimal@0.2.0 +mongo-dev-server@1.1.1 +mongo-id@1.0.9 +npm-mongo@6.16.0 +ordered-dict@1.2.0 +promise@1.0.0 +random@1.2.2 +react-fast-refresh@0.2.9 +react-meteor-data@4.0.0 +reactive-var@1.0.13 +reload@1.3.2 +retry@1.1.1 +routepolicy@1.1.2 +shell-server@0.6.1 +socket-stream-client@0.6.1 +standard-minifier-css@1.9.3 +standard-minifier-js@3.1.1 +static-html@1.4.0 +static-html-tools@1.0.0 +tracker@1.3.4 +typescript@5.6.5 +webapp@2.0.7 +webapp-hashing@1.1.2 +zodern:types@1.0.13 diff --git a/tools/modern-tests/apps/typescript/client/main.css b/tools/modern-tests/apps/typescript/client/main.css new file mode 100644 index 0000000000..7f354f0fa7 --- /dev/null +++ b/tools/modern-tests/apps/typescript/client/main.css @@ -0,0 +1,4 @@ +body { + padding: 10px; + font-family: sans-serif; +} diff --git a/tools/modern-tests/apps/typescript/client/main.html b/tools/modern-tests/apps/typescript/client/main.html new file mode 100644 index 0000000000..7526f5d325 --- /dev/null +++ b/tools/modern-tests/apps/typescript/client/main.html @@ -0,0 +1,8 @@ + + typescript + + + + +
+ diff --git a/tools/modern-tests/apps/typescript/client/main.tsx b/tools/modern-tests/apps/typescript/client/main.tsx new file mode 100644 index 0000000000..523141b528 --- /dev/null +++ b/tools/modern-tests/apps/typescript/client/main.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import { Meteor } from 'meteor/meteor'; +import { App } from '/imports/ui/App'; + +Meteor.startup(() => { + const container = document.getElementById('react-target'); + const root = createRoot(container!); + root.render(); +}); diff --git a/tools/modern-tests/apps/typescript/imports/api/links.ts b/tools/modern-tests/apps/typescript/imports/api/links.ts new file mode 100644 index 0000000000..ec0bf4631f --- /dev/null +++ b/tools/modern-tests/apps/typescript/imports/api/links.ts @@ -0,0 +1,10 @@ +import { Mongo } from 'meteor/mongo'; + +export interface Link { + _id?: string; + title: string; + url: string; + createdAt: Date; +} + +export const LinksCollection = new Mongo.Collection('links'); diff --git a/tools/modern-tests/apps/typescript/imports/ui/App.tsx b/tools/modern-tests/apps/typescript/imports/ui/App.tsx new file mode 100644 index 0000000000..d354e1b352 --- /dev/null +++ b/tools/modern-tests/apps/typescript/imports/ui/App.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { Hello } from './Hello'; +import { Info } from './Info'; + +export const App = () => ( +
+

Welcome to Meteor!

+ + +
+); diff --git a/tools/modern-tests/apps/typescript/imports/ui/Hello.tsx b/tools/modern-tests/apps/typescript/imports/ui/Hello.tsx new file mode 100644 index 0000000000..15e0f185ac --- /dev/null +++ b/tools/modern-tests/apps/typescript/imports/ui/Hello.tsx @@ -0,0 +1,16 @@ +import React, { useState } from 'react'; + +export const Hello = () => { + const [counter, setCounter] = useState(0); + + const increment = () => { + setCounter(counter + 1); + }; + + return ( +
+ +

You've pressed the button {counter} times.

+
+ ); +}; diff --git a/tools/modern-tests/apps/typescript/imports/ui/Info.tsx b/tools/modern-tests/apps/typescript/imports/ui/Info.tsx new file mode 100644 index 0000000000..23cb8f07a3 --- /dev/null +++ b/tools/modern-tests/apps/typescript/imports/ui/Info.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import { useFind, useSubscribe } from "meteor/react-meteor-data"; +import { LinksCollection, Link } from "../api/links"; + +export const Info = () => { + const isLoading = useSubscribe("links"); + const links = useFind(() => LinksCollection.find()); + + if (isLoading()) { + return
Loading...
; + } + + const makeLink = (link: Link) => { + return ( +
  • + { link.title } +
  • + ); + } + + return ( +
    +

    Learn Meteor!

    + +
    + ); +}; diff --git a/tools/modern-tests/apps/typescript/package-lock.json b/tools/modern-tests/apps/typescript/package-lock.json new file mode 100644 index 0000000000..c1df4295f8 --- /dev/null +++ b/tools/modern-tests/apps/typescript/package-lock.json @@ -0,0 +1,1609 @@ +{ + "name": "typescript", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "typescript", + "dependencies": { + "@babel/runtime": "^7.23.5", + "@swc/helpers": "^0.5.17", + "meteor-node-stubs": "^1.2.12", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/mocha": "^8.2.3", + "@types/node": "^22.10.6", + "@types/react": "^18.2.5", + "@types/react-dom": "^18.2.4", + "typescript": "^5.4.5" + } + }, + "node_modules/@babel/runtime": { + "version": "7.24.4", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@types/mocha": { + "version": "8.2.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.17.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.1.tgz", + "integrity": "sha512-y3tBaz+rjspDTylNjAX37jEC3TETEFGNJL6uQDxwF9/8GLLIjW1rvVHlynyuUKMnMr1Roq8jOv3vkopBjC4/VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.12", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.2.75", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.24", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/meteor-node-stubs": { + "version": "1.2.22", + "resolved": "https://registry.npmjs.org/meteor-node-stubs/-/meteor-node-stubs-1.2.22.tgz", + "integrity": "sha512-xpHq9ExdN8fshx9CxOe1nheyWslAepejdkIQgTsk+96e46UJ4ms5TSiQX1rjd6rJuBOjIjHKLqqYvdTsHQj/pw==", + "bundleDependencies": [ + "@meteorjs/crypto-browserify", + "assert", + "browserify-zlib", + "buffer", + "console-browserify", + "constants-browserify", + "domain-browser", + "events", + "https-browserify", + "os-browserify", + "path-browserify", + "process", + "punycode", + "querystring-es3", + "readable-stream", + "stream-browserify", + "stream-http", + "string_decoder", + "timers-browserify", + "tty-browserify", + "url", + "util", + "vm-browserify" + ], + "license": "MIT", + "dependencies": { + "@meteorjs/crypto-browserify": "^3.12.1", + "assert": "^2.1.0", + "browserify-zlib": "^0.2.0", + "buffer": "^5.7.1", + "console-browserify": "^1.2.0", + "constants-browserify": "^1.0.0", + "domain-browser": "^4.23.0", + "events": "^3.3.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "^1.0.1", + "process": "^0.11.10", + "punycode": "^1.4.1", + "querystring-es3": "^0.2.1", + "readable-stream": "^3.6.2", + "stream-browserify": "^3.0.0", + "stream-http": "^3.2.0", + "string_decoder": "^1.3.0", + "timers-browserify": "^2.0.12", + "tty-browserify": "0.0.1", + "url": "^0.11.4", + "util": "^0.12.5", + "vm-browserify": "^1.1.2" + } + }, + "node_modules/meteor-node-stubs/node_modules/@meteorjs/browserify-sign": { + "version": "4.2.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "bn.js": "^5.2.1", + "brorand": "^1.1.0", + "browserify-rsa": "^4.1.0", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash-base": "~3.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1", + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/meteor-node-stubs/node_modules/@meteorjs/browserify-sign/node_modules/isarray": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/@meteorjs/browserify-sign/node_modules/readable-stream": { + "version": "2.3.8", + "inBundle": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/meteor-node-stubs/node_modules/@meteorjs/browserify-sign/node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/@meteorjs/browserify-sign/node_modules/string_decoder": { + "version": "1.1.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/meteor-node-stubs/node_modules/@meteorjs/browserify-sign/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/@meteorjs/create-ecdh": { + "version": "4.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/meteor-node-stubs/node_modules/@meteorjs/create-ecdh/node_modules/bn.js": { + "version": "4.12.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/@meteorjs/crypto-browserify": { + "version": "3.12.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@meteorjs/browserify-sign": "^4.2.3", + "@meteorjs/create-ecdh": "^4.0.4", + "browserify-cipher": "^1.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "diffie-hellman": "^5.0.3", + "hash-base": "~3.0.4", + "inherits": "^2.0.4", + "pbkdf2": "^3.1.2", + "public-encrypt": "^4.0.3", + "randombytes": "^2.1.0", + "randomfill": "^1.0.4" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/asn1.js": { + "version": "4.10.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/meteor-node-stubs/node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/assert": { + "version": "2.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" + } + }, + "node_modules/meteor-node-stubs/node_modules/available-typed-arrays": { + "version": "1.0.7", + "inBundle": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/base64-js": { + "version": "1.5.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/bn.js": { + "version": "5.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/brorand": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/browserify-aes": { + "version": "1.2.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/meteor-node-stubs/node_modules/browserify-cipher": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/meteor-node-stubs/node_modules/browserify-des": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/meteor-node-stubs/node_modules/browserify-rsa": { + "version": "4.1.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "bn.js": "^5.2.1", + "randombytes": "^2.1.0", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/meteor-node-stubs/node_modules/browserify-sign": { + "version": "4.2.3", + "license": "ISC", + "dependencies": { + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.5", + "hash-base": "~3.0", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/meteor-node-stubs/node_modules/browserify-sign/node_modules/bn.js": { + "version": "5.2.1", + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/browserify-sign/node_modules/hash-base": { + "version": "3.0.4", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/meteor-node-stubs/node_modules/browserify-sign/node_modules/readable-stream": { + "version": "2.3.8", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/meteor-node-stubs/node_modules/browserify-sign/node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/browserify-sign/node_modules/string_decoder": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/meteor-node-stubs/node_modules/browserify-sign/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/browserify-zlib": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/meteor-node-stubs/node_modules/buffer": { + "version": "5.7.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/meteor-node-stubs/node_modules/buffer-xor": { + "version": "1.0.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/builtin-status-codes": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/call-bind": { + "version": "1.0.8", + "inBundle": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/meteor-node-stubs/node_modules/call-bound": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/cipher-base": { + "version": "1.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/meteor-node-stubs/node_modules/console-browserify": { + "version": "1.2.0", + "inBundle": true + }, + "node_modules/meteor-node-stubs/node_modules/constants-browserify": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/core-util-is": { + "version": "1.0.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/create-ecdh": { + "version": "4.0.4", + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/meteor-node-stubs/node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/create-hash": { + "version": "1.2.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/meteor-node-stubs/node_modules/create-hmac": { + "version": "1.1.7", + "inBundle": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/meteor-node-stubs/node_modules/define-data-property": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/define-properties": { + "version": "1.2.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/des.js": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/meteor-node-stubs/node_modules/diffie-hellman": { + "version": "5.0.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/meteor-node-stubs/node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/domain-browser": { + "version": "4.23.0", + "inBundle": true, + "license": "Artistic-2.0", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/meteor-node-stubs/node_modules/dunder-proto": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/meteor-node-stubs/node_modules/elliptic": { + "version": "6.5.5", + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/meteor-node-stubs/node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/es-define-property": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/meteor-node-stubs/node_modules/es-errors": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/meteor-node-stubs/node_modules/es-object-atoms": { + "version": "1.1.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/meteor-node-stubs/node_modules/events": { + "version": "3.3.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/meteor-node-stubs/node_modules/evp_bytestokey": { + "version": "1.0.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/meteor-node-stubs/node_modules/for-each": { + "version": "0.3.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/function-bind": { + "version": "1.1.2", + "inBundle": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/get-intrinsic": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/get-proto": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/meteor-node-stubs/node_modules/gopd": { + "version": "1.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/has-property-descriptors": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/has-proto": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/has-symbols": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/has-tostringtag": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/hash-base": { + "version": "3.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/meteor-node-stubs/node_modules/hash.js": { + "version": "1.1.7", + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/meteor-node-stubs/node_modules/hasown": { + "version": "2.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/meteor-node-stubs/node_modules/hmac-drbg": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/meteor-node-stubs/node_modules/https-browserify": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/ieee754": { + "version": "1.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/meteor-node-stubs/node_modules/inherits": { + "version": "2.0.4", + "inBundle": true, + "license": "ISC" + }, + "node_modules/meteor-node-stubs/node_modules/is-arguments": { + "version": "1.2.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/is-callable": { + "version": "1.2.7", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/is-generator-function": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/is-nan": { + "version": "1.3.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/is-regex": { + "version": "1.2.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/is-typed-array": { + "version": "1.1.15", + "inBundle": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/isarray": { + "version": "2.0.5", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/math-intrinsics": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/meteor-node-stubs/node_modules/md5.js": { + "version": "1.3.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/meteor-node-stubs/node_modules/miller-rabin": { + "version": "4.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/meteor-node-stubs/node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/minimalistic-assert": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/meteor-node-stubs/node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/object-inspect": { + "version": "1.13.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/object-is": { + "version": "1.1.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/object-keys": { + "version": "1.1.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/meteor-node-stubs/node_modules/object.assign": { + "version": "4.1.7", + "inBundle": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/os-browserify": { + "version": "0.3.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/pako": { + "version": "1.0.11", + "inBundle": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/meteor-node-stubs/node_modules/parse-asn1": { + "version": "5.1.7", + "inBundle": true, + "license": "ISC", + "dependencies": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/meteor-node-stubs/node_modules/path-browserify": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/pbkdf2": { + "version": "3.1.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "create-hash": "~1.1.3", + "create-hmac": "^1.1.7", + "ripemd160": "=2.0.1", + "safe-buffer": "^5.2.1", + "sha.js": "^2.4.11", + "to-buffer": "^1.2.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/meteor-node-stubs/node_modules/pbkdf2/node_modules/create-hash": { + "version": "1.1.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "sha.js": "^2.4.0" + } + }, + "node_modules/meteor-node-stubs/node_modules/pbkdf2/node_modules/hash-base": { + "version": "2.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1" + } + }, + "node_modules/meteor-node-stubs/node_modules/pbkdf2/node_modules/ripemd160": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "hash-base": "^2.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/meteor-node-stubs/node_modules/possible-typed-array-names": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/meteor-node-stubs/node_modules/process": { + "version": "0.11.10", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/meteor-node-stubs/node_modules/process-nextick-args": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/public-encrypt": { + "version": "4.0.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/meteor-node-stubs/node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/punycode": { + "version": "1.4.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/qs": { + "version": "6.14.0", + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/querystring-es3": { + "version": "0.2.1", + "inBundle": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/meteor-node-stubs/node_modules/randombytes": { + "version": "2.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/meteor-node-stubs/node_modules/randomfill": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/meteor-node-stubs/node_modules/readable-stream": { + "version": "3.6.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/meteor-node-stubs/node_modules/ripemd160": { + "version": "2.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/meteor-node-stubs/node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/safe-regex-test": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/set-function-length": { + "version": "1.2.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/meteor-node-stubs/node_modules/setimmediate": { + "version": "1.0.5", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/sha.js": { + "version": "2.4.11", + "inBundle": true, + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/meteor-node-stubs/node_modules/side-channel": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/side-channel-list": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/side-channel-map": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/side-channel-weakmap": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/stream-browserify": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "node_modules/meteor-node-stubs/node_modules/stream-http": { + "version": "3.2.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + } + }, + "node_modules/meteor-node-stubs/node_modules/string_decoder": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/meteor-node-stubs/node_modules/timers-browserify": { + "version": "2.0.12", + "inBundle": true, + "license": "MIT", + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/meteor-node-stubs/node_modules/to-buffer": { + "version": "1.2.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/meteor-node-stubs/node_modules/tty-browserify": { + "version": "0.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/typed-array-buffer": { + "version": "1.0.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/meteor-node-stubs/node_modules/url": { + "version": "0.11.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "punycode": "^1.4.1", + "qs": "^6.12.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/meteor-node-stubs/node_modules/util": { + "version": "0.12.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/meteor-node-stubs/node_modules/util-deprecate": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/vm-browserify": { + "version": "1.1.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/meteor-node-stubs/node_modules/which-typed-array": { + "version": "1.1.19", + "inBundle": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/meteor-node-stubs/node_modules/xtend": { + "version": "4.0.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/react": { + "version": "18.2.0", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.23.0", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.4.5", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/tools/modern-tests/apps/typescript/package.json b/tools/modern-tests/apps/typescript/package.json new file mode 100644 index 0000000000..7150fb8af6 --- /dev/null +++ b/tools/modern-tests/apps/typescript/package.json @@ -0,0 +1,35 @@ +{ + "name": "typescript", + "private": true, + "scripts": { + "start": "meteor run", + "test": "meteor test --once --driver-package meteortesting:mocha", + "test-app": "TEST_WATCH=1 meteor test --full-app --driver-package meteortesting:mocha", + "visualize": "meteor --production --extra-packages bundle-visualizer" + }, + "dependencies": { + "@babel/runtime": "^7.23.5", + "@swc/helpers": "^0.5.17", + "meteor-node-stubs": "^1.2.12", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/meteor": "^2.9.9", + "@types/mocha": "^8.2.3", + "@types/node": "^22.10.6", + "@types/react": "^18.2.5", + "@types/react-dom": "^18.2.4", + "playwright": "^1.54.2", + "ts-checker-rspack-plugin": "^1.1.5", + "typescript": "^5.4.5" + }, + "meteor": { + "mainModule": { + "client": "client/main.tsx", + "server": "server/main.ts" + }, + "testModule": "tests/main.ts", + "modern": true + } +} diff --git a/tools/modern-tests/apps/typescript/rspack.config.js b/tools/modern-tests/apps/typescript/rspack.config.js new file mode 100644 index 0000000000..64e8db0052 --- /dev/null +++ b/tools/modern-tests/apps/typescript/rspack.config.js @@ -0,0 +1,18 @@ +import { defineConfig } from '@meteorjs/rspack'; +import { TsCheckerRspackPlugin } from 'ts-checker-rspack-plugin'; + +/** + * Rspack configuration for Meteor projects. + * + * Provides typed flags on the `Meteor` object, such as: + * - `Meteor.isClient` / `Meteor.isServer` + * - `Meteor.isDevelopment` / `Meteor.isProduction` + * - …and other flags available + * + * Use these flags to adjust your build settings based on environment. + */ +export default defineConfig(Meteor => { + return { + plugins: [new TsCheckerRspackPlugin()], + }; +}); diff --git a/tools/modern-tests/apps/typescript/server/main.ts b/tools/modern-tests/apps/typescript/server/main.ts new file mode 100644 index 0000000000..5a7ca3abb0 --- /dev/null +++ b/tools/modern-tests/apps/typescript/server/main.ts @@ -0,0 +1,37 @@ +import { Meteor } from 'meteor/meteor'; +import { Link, LinksCollection } from '/imports/api/links'; + +async function insertLink({ title, url }: Pick) { + await LinksCollection.insertAsync({ title, url, createdAt: new Date() }); +} + +Meteor.startup(async () => { + // If the Links collection is empty, add some data. + if (await LinksCollection.find().countAsync() === 0) { + await insertLink({ + title: 'Do the Tutorial', + url: 'https://react-tutorial.meteor.com/simple-todos/01-creating-app.html', + }); + + await insertLink({ + title: 'Follow the Guide', + url: 'https://guide.meteor.com', + }); + + await insertLink({ + title: 'Read the Docs', + url: 'https://docs.meteor.com', + }); + + await insertLink({ + title: 'Discussions', + url: 'https://forums.meteor.com', + }); + } + + // We publish the entire Links collection to all clients. + // In order to be fetched in real-time to the clients + Meteor.publish("links", function () { + return LinksCollection.find(); + }); +}); diff --git a/tools/modern-tests/apps/typescript/tests/main.ts b/tools/modern-tests/apps/typescript/tests/main.ts new file mode 100644 index 0000000000..455a722e9f --- /dev/null +++ b/tools/modern-tests/apps/typescript/tests/main.ts @@ -0,0 +1,21 @@ +import { Meteor } from 'meteor/meteor'; +import assert from 'assert'; + +describe('typescript', function () { + it('package.json has correct name', async function () { + const { name } = await import('../package.json'); + assert.strictEqual(name, 'typescript'); + }); + + if (Meteor.isClient) { + it('client is not server', function () { + assert.strictEqual(Meteor.isServer, false); + }); + } + + if (Meteor.isServer) { + it('server is not client', function () { + assert.strictEqual(Meteor.isClient, false); + }); + } +}); diff --git a/tools/modern-tests/apps/typescript/tsconfig.json b/tools/modern-tests/apps/typescript/tsconfig.json new file mode 100644 index 0000000000..3aa7eac546 --- /dev/null +++ b/tools/modern-tests/apps/typescript/tsconfig.json @@ -0,0 +1,49 @@ +{ + "compilerOptions": { + /* Basic Options */ + "target": "es2018", + "module": "esNext", + "lib": ["esnext", "dom"], + "allowJs": true, + "checkJs": false, + "jsx": "preserve", + "incremental": true, + "noEmit": true, + + /* Strict Type-Checking Options */ + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + + /* Additional Checks */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": false, + "noFallthroughCasesInSwitch": false, + + /* Module Resolution Options */ + "baseUrl": ".", + "paths": { + /* Support absolute /imports/* with a leading '/' */ + "/*": ["*"], + /* Pull in type declarations for Meteor packages from either zodern:types or @types/meteor packages */ + "meteor/*": [ + "node_modules/@types/meteor/*", + ".meteor/local/types/packages.d.ts" + ] + }, + "moduleResolution": "node", + "resolveJsonModule": true, + "types": ["node", "mocha"], + "esModuleInterop": true, + "preserveSymlinks": true + }, + "exclude": [ + "./.meteor/**", + "./packages/**", + "./_build/**", + "./public/_build-bundles/**", + "./public/_build-assets/**", + "./private/_build-assets/**" + ] +} diff --git a/tools/modern-tests/helpers.js b/tools/modern-tests/helpers.js index 6de016a72a..108d313cae 100644 --- a/tools/modern-tests/helpers.js +++ b/tools/modern-tests/helpers.js @@ -18,7 +18,7 @@ const METEOR_EXECUTABLE = path.join(REPO_ROOT, 'meteor'); export async function setupMeteorApp(appName) { // Create a unique temporary directory const randomSuffix = Math.random().toString(36).substring(2, 10); - const tempDir = path.join(os.tmpdir(), `${appName}-${randomSuffix}`); + const tempDir = path.join(os.tmpdir(), `meteortest-${appName}-${randomSuffix}`); // Source app directory const sourceAppDir = path.join(__dirname, 'apps', appName); @@ -230,7 +230,7 @@ export async function runMeteorCommand(command, args = [], cwd, options = {}) { export async function createMeteorApp(appName, example, options = {}) { // Create a unique temporary directory that will be the app directory directly const randomSuffix = Math.random().toString(36).substring(2, 10); - const tempAppName= `${appName}-${randomSuffix}`; + const tempAppName= `meteortest-${appName}-${randomSuffix}`; const tempDir = path.join(os.tmpdir(), tempAppName); console.log(`Creating new Meteor app '${appName}' with example '${example}' in ${tempDir}...`); diff --git a/tools/modern-tests/react.test.js b/tools/modern-tests/react.test.js index f92b6668be..62b23ae724 100644 --- a/tools/modern-tests/react.test.js +++ b/tools/modern-tests/react.test.js @@ -1,22 +1,12 @@ import { killProcessByPort, - setupMeteorApp, - runMeteorApp, cleanupTempDir, killMeteorProcess, - createMeteorApp, - runMeteorCommand, - wait, - appendFileContent, - waitForMeteorOutput, - waitForPlaywrightConsole, - runMeteorTests, - buildMeteorApp + createMeteorApp } from "./helpers"; -import { assertMeteorReactApp, assertRspackScriptTag, assertFileExist } from './assertions'; +import { testMeteorBundler, testMeteorRspackBundler } from './test-helpers'; import fs from 'fs-extra'; import path from 'path'; -import execa from 'execa'; describe('React App Bundling /', () => { describe('Meteor Creator /', () => { @@ -69,277 +59,18 @@ describe('React App Bundling /', () => { }); }); - describe('Meteor Bundler /', () => { - const PORT = 3101; - let meteorProcess; - let tempDir; + describe('Meteor Bundler /', testMeteorBundler({ + appName: 'react', + port: 3101 + })); - beforeAll(async () => { - // Ensure any process on the port is killed - await killProcessByPort(PORT); - - // Setup the Meteor app - tempDir = (await setupMeteorApp('react'))?.tempDir; - }); - - afterAll(async () => { - // Clean up the temporary directory - await cleanupTempDir(tempDir); - }); - - test(`"meteor run" / should start the app`, async () => { - // Run the Meteor app - meteorProcess = (await runMeteorApp(tempDir, PORT))?.meteorProcess; - - // Assert that the Meteor React app is running correctly - await assertMeteorReactApp(PORT); - - // Kill the meteor process - await killMeteorProcess(meteorProcess); - - // Ensure any process on the port is killed - await killProcessByPort(PORT); - }); - }); - - describe('Meteor+Rspack Bundler /', () => { - const PORT = 3102; - let meteorProcess; - let tempDir; - - beforeAll(async () => { - // Ensure any process on the port is killed - await killProcessByPort(PORT); - await killProcessByPort('8080'); - - // Setup the Meteor app - tempDir = (await setupMeteorApp('react'))?.tempDir; - - // Add Rspack package - await runMeteorCommand('add', ['rspack'], tempDir, { checkExitCode: true }); - - // Run the Meteor app to install Rspack - const result = await runMeteorApp(tempDir, PORT, { - waitForOutput: "=> App running at:", - }); - meteorProcess = result.meteorProcess; - - // Wait for a margin - await wait(1000); - - // Assert that the config files exists - await assertFileExist(tempDir, '.gitignore', { content: '_build' }); - await assertFileExist(tempDir, 'rspack.config.js', { content: '@meteorjs/rspack' }); - - // Kill the meteor process - await killMeteorProcess(meteorProcess); - - // Ensure any process on the port is killed - await killProcessByPort(PORT); - await killProcessByPort('8080'); - }); - - afterAll(async () => { - // Clean up the temporary directory - await cleanupTempDir(tempDir); - }); - - test(`"meteor run" / should run and rebuild the app with Rspack`, async () => { - // Run the Meteor app and wait for "restarted at" output - const result = await runMeteorApp(tempDir, PORT, { - waitForOutput: "=> App running at:", - }); - meteorProcess = result.meteorProcess; - - // Wait for a margin - await wait(500); - - // Assert that the app files exists - await assertFileExist(tempDir, '_build/main-dev/client-entry.js'); - await assertFileExist(tempDir, '_build/main-dev/client-rspack.js'); - await assertFileExist(tempDir, '_build/main-dev/client-meteor.js'); - await assertFileExist(tempDir, '_build/main-dev/server-entry.js'); - await assertFileExist(tempDir, '_build/main-dev/server-rspack.js'); - await assertFileExist(tempDir, '_build/main-dev/server-meteor.js'); - - // Assert that the Meteor React app is running correctly - await assertMeteorReactApp(PORT); - - // Assert that the app is using Rspack - await assertRspackScriptTag(PORT, true); - - // Update the client code - await appendFileContent(tempDir, 'client/main.jsx', { - content: 'if (Meteor.isDevelopment) console.log("Hello from dev client");', - }); - await waitForPlaywrightConsole('Hello from dev client'); - - // Update the server code - await appendFileContent(tempDir, 'server/main.js', { - content: 'if (Meteor.isDevelopment) console.log("Hello from dev server");', - }); - await waitForMeteorOutput( - result.outputLines, - 'Hello from dev server' - ); - - // Wait for a margin - await wait(500); - - // Kill the meteor process - await killMeteorProcess(meteorProcess); - - // Ensure any process on the port is killed - await killProcessByPort(PORT); - await killProcessByPort('8080'); - }); - - test(`"meteor run --production" / should run and rebuild the app with Rspack in production`, async () => { - // Run the Meteor app and wait for "restarted at" output - const result = await runMeteorApp(tempDir, PORT, { - waitForOutput: "=> App running at:", - commandOptions: ['--production'], - }); - meteorProcess = result.meteorProcess; - - // Wait for a margin - await wait(500); - - // Assert that the app files exists - await assertFileExist(tempDir, '_build/main-prod/client-entry.js'); - await assertFileExist(tempDir, '_build/main-prod/client-rspack.js'); - await assertFileExist(tempDir, '_build/main-prod/client-meteor.js'); - await assertFileExist(tempDir, '_build/main-prod/server-entry.js'); - await assertFileExist(tempDir, '_build/main-prod/server-rspack.js'); - await assertFileExist(tempDir, '_build/main-prod/server-meteor.js'); - - await assertFileExist(tempDir, 'server/main.js'); - - // Assert that the Meteor React app is running correctly - await assertMeteorReactApp(PORT); - - // Assert that the app is using Rspack - await assertRspackScriptTag(PORT, false); - - // Update the client code - await appendFileContent(tempDir, 'client/main.jsx', { - content: 'if (Meteor.isProduction) console.log("Hello from prod client");', - }); - await waitForPlaywrightConsole('Hello from prod client'); - - // Update the server code - await appendFileContent(tempDir, 'server/main.js', { - content: 'if (Meteor.isProduction) console.log("Hello from prod server");', - }); - await waitForMeteorOutput( - result.outputLines, - 'Hello from prod server' - ); - - // Wait for a margin - await wait(500); - - // Kill the meteor process - await killMeteorProcess(meteorProcess); - - // Ensure any process on the port is killed - await killProcessByPort(PORT); - await killProcessByPort('8080'); - }); - - test(`"meteor test" / should run tests with Rspack`, async () => { - const result = await runMeteorTests(tempDir, PORT, { - waitForOutput: "=> App running at:", - commandOptions: [], - checkTestResults: false, - }); - meteorProcess = result.meteorProcess; - - // Wait for a margin - await wait(500); - - // Assert that the app files exists - await assertFileExist(tempDir, '_build/test/test-entry.js'); - await assertFileExist(tempDir, '_build/test/test-rspack.js'); - await assertFileExist(tempDir, '_build/test/test-meteor.js'); - - // Update the test code - await appendFileContent(tempDir, 'tests/main.js', { - content: 'console.log("Hello from test");', - }); - await waitForMeteorOutput( - result.outputLines, - 'Hello from test' - ); - - // Kill the meteor process - await killMeteorProcess(meteorProcess); - - // Ensure any process on the port is killed - await killProcessByPort(PORT); - }); - - test(`"meteor test --once" / should run tests once with Rspack`, async () => { - // Test the app with Rspack once - await runMeteorTests(tempDir, PORT, { - waitForOutput: "=> App running at:", - commandOptions: ['--once'], - checkTestResults: true, - }); - - // Wait for a margin - await wait(500); - - // Assert that the app files exists - await assertFileExist(tempDir, '_build/test/test-entry.js'); - await assertFileExist(tempDir, '_build/test/test-rspack.js'); - await assertFileExist(tempDir, '_build/test/test-meteor.js'); - - // Ensure any process on the port is killed - await killProcessByPort(PORT); - }); - - test(`"meteor build" / should build the app with Rspack`, async () => { - // Build the app with Rspack - const { buildOutputDir, processResult } = await buildMeteorApp(tempDir, { - commandOptions: ['--directory'], - captureOutput: true - }); - - // Wait for a margin - await wait(500); - - try { - // Assert that the build output directory exists - const buildDirExists = await fs.pathExists(buildOutputDir); - expect(buildDirExists).toBe(true); - - // Assert that the main.js file exists - expect(await fs.pathExists(`${buildOutputDir}/bundle/main.js`)).toBe(true); - - // Assert that the server/package.json file exists - expect(await fs.pathExists(`${buildOutputDir}/bundle/programs/server/package.json`)).toBe(true); - expect(await fs.pathExists(`${buildOutputDir}/bundle/programs/server/program.json`)).toBe(true); - - // Assert that the [web.browser|web.browser.legacy]/program.json file exists - expect(await fs.pathExists(`${buildOutputDir}/bundle/programs/web.browser/program.json`)).toBe(true); - expect(await fs.pathExists(`${buildOutputDir}/bundle/programs/web.browser.legacy/program.json`)).toBe(true); - - // Run npm install in the server directory - console.log('Running npm install in the server directory...'); - const serverDir = path.join(buildOutputDir, 'bundle', 'programs', 'server'); - const npmInstallResult = await execa('npm', ['install'], { - cwd: serverDir, - stdio: 'inherit', - shell: true, - }); - - // Check if the npm install command was successful - expect(npmInstallResult.exitCode).toBe(0); - } finally { - // Clean up the build output directory - await cleanupTempDir(buildOutputDir); - } - }); - }); + describe('Meteor+Rspack Bundler /', testMeteorRspackBundler({ + appName: 'react', + port: 3102, + filePaths: { + client: 'client/main.jsx', + server: 'server/main.js', + test: 'tests/main.js' + }, + })); }); diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js new file mode 100644 index 0000000000..9946b4dd4e --- /dev/null +++ b/tools/modern-tests/test-helpers.js @@ -0,0 +1,375 @@ +/** + * This file contains helper functions for testing Meteor applications. + * It provides reusable test patterns for both the test apps. + */ + +import { + killProcessByPort, + setupMeteorApp, + runMeteorApp, + cleanupTempDir, + killMeteorProcess, + runMeteorCommand, + wait, + appendFileContent, + waitForMeteorOutput, + waitForPlaywrightConsole, + runMeteorTests, + buildMeteorApp +} from "./helpers"; +import { assertMeteorReactApp, assertRspackScriptTag, assertFileExist } from './assertions'; +import fs from 'fs-extra'; +import path from 'path'; +import execa from 'execa'; + +/** + * Helper function to set up and run tests for the Meteor Bundler + * @param {Object} options - Options for the test + * @param {string} options.appName - Name of the app ('react' or 'typescript') + * @param {number} options.port - Port to run the app on + * @param {Function} options.customAssertions - Custom assertions to run after the app is started + * @returns {Function} - Jest test function + */ +export function testMeteorBundler(options) { + const { appName, port, customAssertions } = options; + + return () => { + let meteorProcess; + let tempDir; + + beforeAll(async () => { + // Ensure any process on the port is killed + await killProcessByPort(port); + + // Setup the Meteor app + tempDir = (await setupMeteorApp(appName))?.tempDir; + }); + + afterAll(async () => { + // Clean up the temporary directory + await cleanupTempDir(tempDir); + }); + + test(`"meteor run" / should start the app`, async () => { + // Run the Meteor app + meteorProcess = (await runMeteorApp(tempDir, port))?.meteorProcess; + + // Assert that the Meteor app is running correctly + await assertMeteorReactApp(port, { title: appName }); + + // Run custom assertions if provided + if (customAssertions) { + await customAssertions({ tempDir, port, meteorProcess }); + } + + // Kill the meteor process + await killMeteorProcess(meteorProcess); + + // Ensure any process on the port is killed + await killProcessByPort(port); + }); + }; +} + +/** + * Helper function to set up and run tests for the Meteor+Rspack Bundler + * @param {Object} options - Options for the test + * @param {string} options.appName - Name of the app ('react', 'typescript', etc) + * @param {number} options.port - Port to run the app on + * @param {Object} options.filePaths - File paths for the app + * @param {string} options.filePaths.client - Client file path (e.g., 'client/main.jsx') + * @param {string} options.filePaths.server - Server file path (e.g., 'server/main.js') + * @param {string} options.filePaths.test - Test file path (e.g., 'tests/main.js') + * @param {Object} options.customMessages - Custom messages for console logs + * @param {string} options.customMessages.devClient - Message for development client + * @param {string} options.customMessages.devServer - Message for development server + * @param {string} options.customMessages.prodClient - Message for production client + * @param {string} options.customMessages.prodServer - Message for production server + * @param {string} options.customMessages.test - Message for test + * @param {Function} options.customAssertions - Custom assertions to run after each test + * @returns {Function} - Jest test function + */ +export function testMeteorRspackBundler(options) { + const { + appName, + port, + filePaths = { + client: 'client/main.jsx', + server: 'server/main.js', + test: 'tests/main.js' + }, + customMessages = { + devClient: "Hello from dev client", + devServer: "Hello from dev server", + prodClient: "Hello from prod client", + prodServer: "Hello from prod server", + test: "Hello from test" + }, + customAssertions + } = options; + + return () => { + let meteorProcess; + let tempDir; + + beforeAll(async () => { + // Ensure any process on the port is killed + await killProcessByPort(port); + await killProcessByPort('8080'); + + // Setup the Meteor app + tempDir = (await setupMeteorApp(appName))?.tempDir; + + // Add Rspack package + await runMeteorCommand('add', ['rspack'], tempDir, { checkExitCode: true }); + + // Run the Meteor app to install Rspack + const result = await runMeteorApp(tempDir, port, { + waitForOutput: "=> App running at:", + }); + meteorProcess = result.meteorProcess; + + // Wait for a margin + await wait(1000); + + // Assert that the config files exists + await assertFileExist(tempDir, '.gitignore', { content: '_build' }); + await assertFileExist(tempDir, 'rspack.config.js', { content: '@meteorjs/rspack' }); + + // Kill the meteor process + await killMeteorProcess(meteorProcess); + + // Ensure any process on the port is killed + await killProcessByPort(port); + await killProcessByPort('8080'); + }); + + afterAll(async () => { + // Clean up the temporary directory + await cleanupTempDir(tempDir); + }); + + test.only(`"meteor run" / should run and rebuild the app with Rspack`, async () => { + // Run the Meteor app and wait for "restarted at" output + const result = await runMeteorApp(tempDir, port, { + waitForOutput: "=> App running at:", + }); + meteorProcess = result.meteorProcess; + + // Wait for a margin + await wait(500); + + // Assert that the app files exists + await assertFileExist(tempDir, '_build/main-dev/client-entry.js'); + await assertFileExist(tempDir, '_build/main-dev/client-rspack.js'); + await assertFileExist(tempDir, '_build/main-dev/client-meteor.js'); + await assertFileExist(tempDir, '_build/main-dev/server-entry.js'); + await assertFileExist(tempDir, '_build/main-dev/server-rspack.js'); + await assertFileExist(tempDir, '_build/main-dev/server-meteor.js'); + + // Assert that the Meteor app is running correctly + await assertMeteorReactApp(port, { title: appName }); + + // Assert that the app is using Rspack + await assertRspackScriptTag(port, true); + + // Update the client code + await appendFileContent(tempDir, filePaths.client, { + content: `if (Meteor.isDevelopment) console.log("${customMessages.devClient}");`, + }); + await waitForPlaywrightConsole(customMessages.devClient); + + // Update the server code + await appendFileContent(tempDir, filePaths.server, { + content: `if (Meteor.isDevelopment) console.log("${customMessages.devServer}");`, + }); + await waitForMeteorOutput( + result.outputLines, + customMessages.devServer + ); + + // Run custom assertions if provided + if (customAssertions && customAssertions.afterRun) { + await customAssertions.afterRun({ tempDir, port, meteorProcess, result }); + } + + // Wait for a margin + await wait(500); + + // Kill the meteor process + await killMeteorProcess(meteorProcess); + + // Ensure any process on the port is killed + await killProcessByPort(port); + await killProcessByPort('8080'); + }); + + test(`"meteor run --production" / should run and rebuild the app with Rspack in production`, async () => { + // Run the Meteor app and wait for "restarted at" output + const result = await runMeteorApp(tempDir, port, { + waitForOutput: "=> App running at:", + commandOptions: ['--production'], + }); + meteorProcess = result.meteorProcess; + + // Wait for a margin + await wait(500); + + // Assert that the app files exists + await assertFileExist(tempDir, '_build/main-prod/client-entry.js'); + await assertFileExist(tempDir, '_build/main-prod/client-rspack.js'); + await assertFileExist(tempDir, '_build/main-prod/client-meteor.js'); + await assertFileExist(tempDir, '_build/main-prod/server-entry.js'); + await assertFileExist(tempDir, '_build/main-prod/server-rspack.js'); + await assertFileExist(tempDir, '_build/main-prod/server-meteor.js'); + + await assertFileExist(tempDir, filePaths.server); + + // Assert that the Meteor app is running correctly + await assertMeteorReactApp(port, { title: appName }); + + // Assert that the app is using Rspack + await assertRspackScriptTag(port, false); + + // Update the client code + await appendFileContent(tempDir, filePaths.client, { + content: `if (Meteor.isProduction) console.log("${customMessages.prodClient}");`, + }); + await waitForPlaywrightConsole(customMessages.prodClient); + + // Update the server code + await appendFileContent(tempDir, filePaths.server, { + content: `if (Meteor.isProduction) console.log("${customMessages.prodServer}");`, + }); + await waitForMeteorOutput( + result.outputLines, + customMessages.prodServer + ); + + // Run custom assertions if provided + if (customAssertions && customAssertions.afterRunProduction) { + await customAssertions.afterRunProduction({ tempDir, port, meteorProcess, result }); + } + + // Wait for a margin + await wait(500); + + // Kill the meteor process + await killMeteorProcess(meteorProcess); + + // Ensure any process on the port is killed + await killProcessByPort(port); + await killProcessByPort('8080'); + }); + + test(`"meteor test" / should run tests with Rspack`, async () => { + const result = await runMeteorTests(tempDir, port, { + waitForOutput: "=> App running at:", + commandOptions: [], + checkTestResults: false, + }); + meteorProcess = result.meteorProcess; + + // Wait for a margin + await wait(500); + + // Assert that the app files exists + await assertFileExist(tempDir, '_build/test/test-entry.js'); + await assertFileExist(tempDir, '_build/test/test-rspack.js'); + await assertFileExist(tempDir, '_build/test/test-meteor.js'); + + // Update the test code + await appendFileContent(tempDir, filePaths.test, { + content: `console.log("${customMessages.test}");`, + }); + await waitForMeteorOutput( + result.outputLines, + customMessages.test + ); + + // Run custom assertions if provided + if (customAssertions && customAssertions.afterTest) { + await customAssertions.afterTest({ tempDir, port, meteorProcess, result }); + } + + // Kill the meteor process + await killMeteorProcess(meteorProcess); + + // Ensure any process on the port is killed + await killProcessByPort(port); + }); + + test(`"meteor test --once" / should run tests once with Rspack`, async () => { + // Test the app with Rspack once + const result = await runMeteorTests(tempDir, port, { + waitForOutput: "=> App running at:", + commandOptions: ['--once'], + checkTestResults: true, + }); + + // Wait for a margin + await wait(500); + + // Assert that the app files exists + await assertFileExist(tempDir, '_build/test/test-entry.js'); + await assertFileExist(tempDir, '_build/test/test-rspack.js'); + await assertFileExist(tempDir, '_build/test/test-meteor.js'); + + // Run custom assertions if provided + if (customAssertions && customAssertions.afterTestOnce) { + await customAssertions.afterTestOnce({ tempDir, port, result }); + } + + // Ensure any process on the port is killed + await killProcessByPort(port); + }); + + test(`"meteor build" / should build the app with Rspack`, async () => { + // Build the app with Rspack + const { buildOutputDir, processResult } = await buildMeteorApp(tempDir, { + commandOptions: ['--directory'], + captureOutput: true + }); + + // Wait for a margin + await wait(500); + + try { + // Assert that the build output directory exists + const buildDirExists = await fs.pathExists(buildOutputDir); + expect(buildDirExists).toBe(true); + + // Assert that the main.js file exists + expect(await fs.pathExists(`${buildOutputDir}/bundle/main.js`)).toBe(true); + + // Assert that the server/package.json file exists + expect(await fs.pathExists(`${buildOutputDir}/bundle/programs/server/package.json`)).toBe(true); + expect(await fs.pathExists(`${buildOutputDir}/bundle/programs/server/program.json`)).toBe(true); + + // Assert that the [web.browser|web.browser.legacy]/program.json file exists + expect(await fs.pathExists(`${buildOutputDir}/bundle/programs/web.browser/program.json`)).toBe(true); + expect(await fs.pathExists(`${buildOutputDir}/bundle/programs/web.browser.legacy/program.json`)).toBe(true); + + // Run npm install in the server directory + console.log('Running npm install in the server directory...'); + const serverDir = path.join(buildOutputDir, 'bundle', 'programs', 'server'); + const npmInstallResult = await execa('npm', ['install'], { + cwd: serverDir, + stdio: 'inherit', + shell: true, + }); + + // Check if the npm install command was successful + expect(npmInstallResult.exitCode).toBe(0); + + // Run custom assertions if provided + if (customAssertions && customAssertions.afterBuild) { + await customAssertions.afterBuild({ tempDir, buildOutputDir, processResult }); + } + } finally { + // Clean up the build output directory + await cleanupTempDir(buildOutputDir); + } + }); + }; +} diff --git a/tools/modern-tests/typescript.test.js b/tools/modern-tests/typescript.test.js new file mode 100644 index 0000000000..ec4cda359a --- /dev/null +++ b/tools/modern-tests/typescript.test.js @@ -0,0 +1,33 @@ +import { + waitForMeteorOutput +} from "./helpers"; +import { testMeteorBundler, testMeteorRspackBundler } from './test-helpers'; + +describe('TypeScript App Bundling /', () => { + describe('Meteor Bundler /', testMeteorBundler({ + appName: 'typescript', + port: 3201 + })); + + describe('Meteor+Rspack Bundler /', testMeteorRspackBundler({ + appName: 'typescript', + port: 3202, + filePaths: { + client: 'client/main.tsx', + server: 'server/main.ts', + test: 'tests/main.ts' + }, + customAssertions: { + afterRun: async ({ result }) => { + await waitForMeteorOutput( + result.outputLines, + /.*\[Rspack Client].*No TypeScript errors found\./ + ); + await waitForMeteorOutput( + result.outputLines, + /.*\[Rspack Server].*No TypeScript errors found\./ + ); + }, + } + })); +});