Giter Club home page Giter Club logo

wmr's Introduction

WMR

Warning

WMR is unfortunately no longer maintained. In its place, we'd recommend Vite with @preactjs/preset-vite. It offers many of the same features (like the prerendering API) but is much more robust and up to date. Thanks to all contributors and users over the years!

wmr logo

npm install size OpenCollective Backers OpenCollective Sponsors

The tiny all-in-one development tool for modern web apps, in a single 2mb file with no dependencies.

All the features you'd expect and more, from development to production:

πŸ”¨ Β  No entry points or pages to configure - just HTML files with <script type=module>
🦦   Safely import "packages" from npm without installation
πŸ“¦ Β  Smart bundling and caching for npm dependencies
↻ Β  Hot reloading for modules, Preact components and CSS
⚑️   Lightning-fast JSX support that you can debug in the browser
πŸ’„ Β  Import CSS files and CSS Modules (*.module.css)
πŸ”© Β  Out-of-the-box support for TypeScript
πŸ“‚ Β  Static file serving with hot reloading of CSS and images
πŸ—œ Β  Highly optimized Rollup-based production output (wmr build)
πŸ“‘ Β  Crawls and pre-renders your app's pages to static HTML at build time
🏎   Built-in HTTP2 in dev and prod (wmr serve --http2)
πŸ”§ Β  Supports Rollup plugins, even in development where Rollup isn't used

Quickstart (recommended)

Create a new project in seconds using create-wmr:

npm init wmr your-project-name

or

yarn create wmr your-project-name

illustration of installation to build for wmr

πŸ’ If you'd like ESLint to be set up for you, add --eslint to the command. Note: this will use 150mb of disk space.

Check out the docs to learn more

Packages

Package Description Version
wmr Tiny all-in-one development tool for modern web apps wmr npm
create-wmr Create a new WMR project in seconds create-wmr npm
@wmrjs/directory-import Import a directory's files as an Array @wmrjs/directory-import npm
@wmrjs/nomodule Generate legacy fallback bundles for older browsers @wmrjs/nomodule npm
@wmrjs/service-worker Bundle service workers @wmrjs/service-worker npm
preact-iso Optimal code-splitting, hydration and routing for Preact preact-iso npm

Contributing

git clone [email protected]:preactjs/wmr.git
cd wmr
yarn

# run the demo (no compile)
yarn demo serve

# build and serve the demo for prod
yarn demo build:prod && yarn demo serve:prod

# build the single-file CLI:
yarn workspace wmr build

Adding a changeset

Don't forget to also include a changeset, by running this command at the root of the project:

yarn changeset

This will take you through a process of selecting the changed packages, the version updates and a description of the change. Afterwards, changesets, will generate a .md file inside a .changeset directory. Please commit that file as well.

After all that, you are good to go. πŸ‘

wmr's People

Contributors

aduh95 avatar ajayposhak avatar andrewiggins avatar calebeby avatar codepunkt avatar cristianbote avatar dbetteridge avatar developit avatar emiltholin avatar fabiosantoscode avatar forsakenharmony avatar github-actions[bot] avatar hassanbazzi avatar intrnl avatar inviz avatar jbt avatar joebeachjoebeach avatar jovidecroock avatar lpadier avatar lukeed avatar marvinhagemeister avatar matiasperz avatar michael-erskine avatar piotr-cz avatar rschristian avatar smithbm2316 avatar sync avatar takurinton avatar wilberforce avatar witaka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wmr's Issues

Add production build/export

Implement production-quality build/export. This should disable Rollup's preserveModules mode and enable Terser.

SyntaxError with JSX props

Input:

import { h, render } from "preact";

function Foo(props) {
	return <div>{props.foo}</div>;
}

render(
  <Foo foo={<div />} />,
  document.getElementById("app")
);

WMR output (only tried dev mode):

import { h, render } from "preact";

function Foo(props) {
	return html`<div>${props.foo}</div>`;
}

render(
  html`<${Foo} foo=${<div />} />, // <-- SyntaxError
  document.getElementById("app")
);

Expected output:

import { h, render } from "preact";

function Foo(props) {
	return html`<div>${props.foo}</div>`;
}

render(
  html`<${Foo} foo=${html`<div />`} />,
  document.getElementById("app")
);

Add option to disable jsx -> htm conversion

I'm using wmr in the test suite for our Preact Devtools extension and I need to verify that it works with a range of Preact versions.

To do so I've set up multiple aliases:

{
  "alias": {
    "[email protected]": "/vendor/preact-10.0.0-rc.0/preact.js",
    "[email protected]": "/vendor/preact.10.3.4/preact.js"
}

And than we can use that with standard imports:

import { h, render } from "[email protected]";

render(
  <p>Rendered with 10.3.4</p>,
  document.getElementById("app")
);

This works really well except for when I use JSX. JSX is transpiled to htm template strings, but by adding a import htm from "htm/preact" import, an additional version of Preact is pulled in.

import { html } from "/@npm/htm/preact"; // Pulls in wrong version
import { h, render } from "[email protected]";

render(
  html`<p>Rendered with 10.3.4</p>`,
  document.getElementById("app")
);

To solve this I can come up with two options:

  1. Transpile JSX to createElement calls

This is arguable the more verbose version, but it works. The parsing performance benefit of tagged template strings doesn't really come to play in my set up, so I'm good with createElement calls.

import { h, render } from "[email protected]";

render(
  h("p", null, "Rendered with 10.3.4"),
  document.getElementById("app")
);
  1. Inject htm.bind(h) calls into every file

Another option could be to drop the htm/preact import and initialize the htm constructor anew in each file:

import htm from "/@npm/htm";
import { h, render } from "[email protected]";

const html = htm.bind(h);

render(
  html`<p>Rendered with 10.3.4</p>`,
  document.getElementById("app")
);
  1. Transpile to html calls, but don't inject htm.bind(h) automatically

This closely ties to 2) and is probably a lot more flexible for the end user. Essentially he'd need to make sure that an html function is present in the module scope themselves.

Type resolution for IDE autocompletion

The live-bundler bypasses node_modules which is the assumed default type root to resolve types from in TypeScript. Since the dependencies are not there, users won't enjoy the benefits of type-hints and autocompletion in their IDE/Editor.

There are some flags for the TS compiler that can change that and some investigation is needed to know if that would work out. For packages where the types are bundled and not extracted to @types/my-package we could enhance the unpkg module to check for the types or typings key in package.json and download that too.

Spread Terser+Brotli npm upgrade work out over time

The development middleware handles /@npm/FOO dependency requests as fast as possible by returning unminified responses the first time a dependency is requested, and applying only basic gzip compression to responses above a size threshold. As an optimization, all individual dependencies requested are placed into a queue that slowly upgrades their cached code (on disk and in-memory) by minifying it and applying Brotli compression (node's native implementation with q11 setting).

This operation ends up being quite expensive, and for projects with a lot of dependencies it tends to spike CPU usage for a little while after the first time you load the application in a browser (not after starting wmr in dev mode). This happens because the queue is rudimentary, simply processing each dependency in a loop with a 1-second delay between entries:

https://github.com/developit/wmr/blob/cb10267228ea8f02119c0465b7174e0752416828/src/lib/npm-middleware-cache.js#L72-L89

This actually ends up bogging Glitch down, since the 1-second delay is short enough that synchronous Terser passes on deps block the event loop when there are still more dependency requests to be handled by WMR.

Ideally, we can solve this by doing three things:

    • reduce the Brotli quality a bit.

    This could be a user setting, or it could use simple detection of low compression rate (bytes/ms) to automatically reduce quality to prevent high CPU usage.

  1. substantially increase the time required before the optimizer queue processes a dependency.

    Idea: We could create a requestIdleCallback-like function, which lazily calls a callback after a period where no work has been done. To track "work being done", all incoming network requests could increment a centralized pendingWork counter, then decrement it when the request has been handled. Idle time would be the time spent at pendingWork==0.

  2. switch to off-main-thread Terser (since Terser is synchronous its now at least theoretically async)

    Idea: we could create bundle a single terser-worker.js file that includes a copy of Terser and provides a postMessage RPC API that we can then use for all three cases where Terser is currently being used on the main thread: production bundling, our own wmr.cjs bundling, and development npm dep upgrades. It would also move Terser out of the main bundle, saving around 80kb.

It may also be worth considering a flag that turns automatic dependency optimization off: it's a nicety, and for folks on constrained environments like Glitch it may not be worth the overhead.

Checklist

  • Implement a true idle queue based on observation of request handling
  • Move Terser into its own bundle and load it in a Worker (worker_threads is Node 10+, which is fine)
  • Reduce Brotli quality if compression is slow (below a "fast machine" threshold we set)
  • [maybe] Add an --optimize-dependencies flag (update: --compress false now triggers this)

TS transpile failing causes HMR to "stick"

I am noticing this when editing JSX in a TS file. Not sure of the ideal fix, but a quick fix might be to have the Sucrase plugin catch errors and instead emit warnings during development.

[build] Heuristic-based chunking/splitting

Rollup doesn't do heuristic-based chunking by default, but folks generally want it in production. This might be a lot less important for multi-entry / multi-page sites, but for single-entry (currently the only thing wmr supports), we're just always generating one bundle (except for dynamic imports).

I believe it should be possible to use manualChunks for this.

Use es-module-lexer for JS import parsing

In unbundled mode, we currently use a regular expression to detect and rewrite imports (and dynamic imports?):
https://github.com/developit/wmr/blob/57be6e663dd1911bce6fd4ca9d0ac95792679022/src/wmr-middleware.js#L176-L193

It should be possible to switch to es-module-lexer, which is 4kb and can be embedded into our bundle. It'd look something like:

import { init, parse } from 'es-module-lexer/dist/lexer.js';
// ...
// wmr-middleware.js#L176-L193:

		code = code.replace(
			/\bimport\.meta\.([a-zA-Z_$][a-zA-Z0-9_$]*)\b/g,
			(str, property) => NonRollup.resolveImportMeta(property) || str
		);

		await init;

		const [imports] = parse(code, id);
		let out = '';
		let offset = 0;
		for (const import of imports) {
			out += code.substring(offset, import.s);
			let spec = code.substring(import.s, import.e);
			let q = '';
			if (import.d === -2) {
				// import.meta
				const property = spec.match(/\.([a-zA-Z_$][a-zA-Z0-9_$]*)\s*$/g)[1];
				out += NonRollup.resolveImportMeta(property) || spec;
				continue;
			}
			if (import.d > -1) {
				// dynamic import (specifier includes quotes)
				q = spec.match(/^\s*(['"`])/g)[1];
				if (!q) {
					console.warn(`Cannot resolve dynamic expression in import(${spec})`);
					out += spec;
					continue;
				}
				spec = spec.replace(/^\s*(['"`])(.*)\1\s*$/g, '$2');
			}
			if (spec.endsWith('.css')) spec += '.js';
			if (spec === 'wmr') {
				spec = '/_wmr.js';
			} else if (spec[0] !== '.' && spec[0] !== '/') {
				spec = `/@npm/${spec}`;
			}
			out += q + spec + q;
		);
		out += code.substring(offset);

		code = out;

Module/nomodule output

It is now possible to use multiple outputs (but the same rollup compilation) to generate bundles with different manualChunks option.

Also enabled for multi-output in rollup/rollup#3645, the inlineDynamicImports option might make it easy for us to generate an ssr-bundle.js quickly without adding much overhead to prod builds.

Consider changing .dist/ to dist/

The most popular way according to GitHub stars is to copy & paste one from https://github.com/github/gitignore/blob/master/Node.gitignore . There is even a vscode plugin for that, that does this (I'm using it myself quite heavily). It ignores dist/ by default.

I know we went a bit back and forth on naming it dist/ vs .dist/ but the former arguable removes a friction point for users. Let's not repeat the same mistake in microbundle of requiring users to extend their .gitignore.

Unable to import .json files

I guess this is not implemented in the spec yet. Not sure if we should patch that or not. To me it seems like patching it by converting it to a .js file until the spec is stable sounds like a reasonable thing to do.

Failed to load module script: The server responded with a non-JavaScript MIME type of "application/json". Strict MIME type checking is enforced for module scripts per HTML spec.

Print resolved file on 404

Currently when a file is not found we log out the following to the user's terminal:

404 /extension/installHook.css - Error: File not found
404 /installHook.ts - Error: File not found
404 /extension/setup.css - Error: File not found
404 /extension/setup.js - Error: File not found

As a user it's hard to know where the url was resolved to. We can help them by logging which file wmr tried to read. Maybe we should swap the url in the error message for the actual file path?

Print user actionable error when public dir is missing

If there is no public/ directory in the project, the server will serve the text string [object Object]. The cli prints:

$ wmr start
Listening on http://localhost:8081
  βŒ™ http://192.168.178.45:8081
{ code: 404 }

It prints only the status code and makes no mention of which file/url returned a 404 and therefore no actionable hint for the user what he needs to do to resolve the situation.

Unable to transpile JSX

This only happens in production, NOT in development.

export function FOo() {
	return (
		<div>
			{Object.keys({}).map(key => {
				return <span />;
			})}
		</div>
	);
}

Error:

Error: Unknown AST Node type: ReturnStatement
    at codegen (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:171:8)
    at Array.map (<anonymous>)
    at codegen (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:89:25)
    at codegen (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:165:76)
    at Array.map (<anonymous>)
    at codegen (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:167:53)
    at codegen (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:140:20)
    at codegen (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:134:31)
    at generate (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:52:9)
    at Path._regenerate (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:351:13)
    at Path._regenerateParent (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:361:7)
    at Path.replaceWith (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:312:12)
    at processNode (/home/marvinh/dev/github/wmr/node_modules/babel-plugin-transform-jsx-to-htm/dist/babel-plugin-transform-jsx-to-htm.js:206:12)
    at jsxVisitorHandler (/home/marvinh/dev/github/wmr/node_modules/babel-plugin-transform-jsx-to-htm/dist/babel-plugin-transform-jsx-to-htm.js:225:7)
    at JSXElement (/home/marvinh/dev/github/wmr/node_modules/babel-plugin-transform-jsx-to-htm/dist/babel-plugin-transform-jsx-to-htm.js:248:9)
    at Object.enter (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:736:5)
    at enter (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:517:14)
    at enter (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:535:9)
    at enter (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:539:11)
    at enter (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:535:9)
    at enter (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:539:11)
    at enter (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:539:11)
    at enter (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:535:9)
    at Object.visit (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:554:3)
    at transform (file:///home/marvinh/dev/github/wmr/src/lib/acorn-traverse.js:659:2)
    at Object.transform (file:///home/marvinh/dev/github/wmr/src/plugins/htm-plugin.js:45:16)
    at file:///home/marvinh/dev/github/wmr/node_modules/rollup/dist/es/shared/rollup.js:18578:25

Add support for multiple cwd/asset paths

Most client projects I've worked on in the past year had the following directory structure:

/my-project
  /public <-- cwd
    favicon.ico
    index.html <-- should reference /src/index.js
  /src
    /components
      foo.js
    index.js <-- main entry file
  package.json

Afaik this is the default of CRA and other cli's too. Haven't checked next.js.

This structure breaks wmr's current assumption that everything is inside the specified cwd, as the files are "scattered" across /public and /src.

Over at in the our devtools code-base I'm facing a similar scenario:

/preact-devtools
  /src
    index.ts <-- main devtools entry
  /tests
    /fixtures <-- cwd
      page1.js
      page2.js
      index.html <-- should load /src/index.ts

A solution to that could be to be able to specify multiple cwd paths that are valid to be served from. On resolution it would iterate all cwd's and check if the requested file is present.

Add support for class fields

Any project that uses classes that I have access to, uses class fields. I've checked with other framework clis (CRA, vue-cli, angular, ember), and they all support them.

class MyComponent extends Component {
  state = {
    foo: false,
  }

  timeout = null

  onClick = () => {}

  render() {
    return <div />
  }
}

Compatibility list

I've been trying out wmr in some existing client projects and I'll use this issue as a list to keep track of changes I had to make. Mostly to be aware how we could ease migration to switch to wmr from another build tool.

  • Class components typically use class fields #126
  • Importing image files returns the file path as the default export #133
  • @urql/core import fails. See #129 and #139 (PR)
  • React projects often have a loader for .svg files, that returns a ReactComponent export
  • Node-resolution like /foo -> /foo/index.js, imports without extension basically #138
  • Error overlay

Allow Configuration

We need to decide whether this should be rollup.config.js or something else like wmr.config.js.

Since our plugin API aims to be compatible with Rollup and we use Rollup in prod, I'm leaning towards "it's just a rollup.config.js", but we need to make sure there won't be some reason to extend that configuration to include WMR-specific stuff - that would be strange to add into what is supposed to be a Rollup config.

To Do

  • Decide between rollup.config.js or wmr.config.js
    Resolution: wmr.config.js, possibly optional fall back to rollup.config.js (not v0?)

    Do we think we'll need more than pure Rollup configuration?
    We talked about middleware support: can that go in another file?

  • Implement config loading (implemented in #179)

    In Node 12.8, load the file using import("x.config.js"), enforcing ESM.
    In non-ESM environments, run the file through fast-cjs-plugin then require() it.

  • Establish a standard for config to handle "dev" vs "prod"

    We could set process.env.NODE_ENV=".." before loading the config.

  • Find/write example plugins to test dev mode

    Examples: cosmiconfig support

Support specifying npm dependency versions in package.json

Currently, if using npm to install dependencies, we use whatever versions npm decided on. This is nice since it retains the ability to lock to a version, get updates, etc.

When using auto-installation (no deps specified in package.json), unless a version is manually specified in the import statement (import '[email protected]/hooks'), we assume latest. I ran into a situation today that illustrated why this is not sufficient: rmcw only works with material-components-web@5, and since that library seems to follow a release methodology of "only release breaking changes, otherwise it's a canary build", this fails badly (the current version is 7 and they changed the API considerably).

I'd normally suggest we have the npm dependency resolve inspect the package.json versions and do its existing semver logic to figure out a version. That's a good start, but I'd also like to have it respect peerDependencies - on Glitch, dependencies and devDependencies get auto-installed if specified in the package.json. This takes ages and consumes a ton of disk space, and doesn't do the nice lazy-installation thing WMR does out-of-the-box.

So I'm thinking in addition to respecting versions specified in dependencies and devDependencies, we could also look for versions in peerDependencies, or even bundledDependencies. Thoughts?

Invalid import statement added to non-module script tags

It seems like there is an issue with non-module script tags. The linked file will be transformed as if it was a module.

<script src="./preact.umd.js"></script>

Error message:

Uncaught SyntaxError: import declarations may only appear at top level of a module

Output of transformed code:

import * as $cjs$preact from "/@npm/preact";var module={exports:{}},exports=module.exports;!(function (n, t) {
	"object" == typeof exports && "undefined" != typeof module
		? t(exports, ($cjs$preact&&$cjs$preact.default||Object.defineProperty($cjs$preact||{},'default',{configurable:true,value:$cjs$preact})))
		: "function" == typeof define && define.amd
		? define(["exports", "preact"], t)
		: t((n.preactHooks = {}), n.preact);
})(this, function (n, t) {
// ...

Most likely a user wants to include third party libraries when they have a no-module file. The easiest fix is to probably just serve them directly and ignore any transforms.

[bundling] Resolve entries based on index.html

I've just created an empty repo with an public/index.html, but wmr immediately errors because there is no js file present.

/my-project
  /public
    index.html

Content of index.html:

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf8" />
		<title>Test</title>
	</head>
	<body>
		<h1>Hello wmr</h1>
		<div id="root"></div>
	</body>
</html>
Listening on http://localhost:8080
  βŒ™ http://192.168.2.107:8080
Error(UNRESOLVED_ENTRY): Could not resolve entry module (./public/index.js).

The error occurs because we immediately try to run rollup with an assumed public/index.js entry file:

https://github.com/developit/wmr/blob/377c8bff70cbf725c3ccb8b959c913c7dc75c53c/src/bundler.js#L30-L31

We probably shouldn't throw an error there and lazily start rollup based on when a script is referenced in index.html. That would be similar to how parcel and vite work.

Add `--cwd` + `--base` cli options

Following our conversation from today from slack:

  • --base -> web root, default is /public
  • --cwd -> the root directory to work in, default is process.cwd()

Lots of current code uses cwd for everything. We need to revisit which places need base and which `cwd.

TypeError with shorthand JSX Fragments

The following code leads to a TypeError:

<>foo</>

Message (only displayed in browser console):

Uncaught (in promise) TypeError: error loading dynamically imported module

Rework config loading

Copying from slack:

  • load configs up-front before calling into build() or start()
  • start() sets up file watcher for package.json. Successfully parsed file changes emit a configChange event on options.bus
  • middleware() listens for that event, and invokes a special configUpdated(newConfig) hook on all plugins.

(If necessary, we can also listen for the event and fire the hook in the second RollupPluginContainer used by the CSS middleware)

this also nicely keeps the initial package.json + wmr.config.js parsing as a one-time (non-watch) thing that happens prior to build()/start().

Switch CommonJS transform to cjs-module-lexer

Now that cjs-module-lexer is battle-tested (it powers CommonJS in Node as of 14.17), we should switch to it. This gives us a clean way to support CJS-->ESM transformations like we're already doing, but without the horrible regex-based implementation I cobbled together.

  • inline my forked copy of cjs-module-lexer's JS version with require() tracking
  • create a new lib/transform-cjs.js module that wraps cjs-module-lexer (similar to lib/transform-imports.js)
  • replace fast-cjs-plugin.js implementation with a call to the new library
  • add/improve CommonJS tests
    (improved in #156)

Add support for "alias" field

Parcel allows an "alias" package.json field, which would be nice:

{
  "alias": {
    "react": "preact/compat",
    "react-dom": "preact/compat"
  }
}

For now, npm: aliases actually work nicely (though may duplicate non-aliased imports for their during dev):

{
  "dependencies": {
    "react": "npm:@preact/compat",
    "react-dom": "npm:@preact/compat"
  }
}

Resolve nested "browser" field

Follow-up to #127.

Some packages use the browser field to rewrite import paths. This is usually done to swap out node-specific code with browser-based code. There is no resolution going on, it's straight up string replacement.

{
  "name": "axios",
  "browser": {
    "./adapters/http": "./adapters/xhr"
  }
}

EDIT: uuid is another package that needs this:

"browser": {
  "./lib/rng.js": "./lib/rng-browser.js",
  "./lib/sha1.js": "./lib/sha1-browser.js",
  "./lib/md5.js": "./lib/md5-browser.js"
},

Process shim throwing error

// index.js
console.log(process.env.NODE_ENV)

Error message:

500 ./public/index.js - Error: Invalid specifier: process.js
    at file:///Users/m.hagemeister/dev/github/wmr/src/plugins/npm-plugin/index.js:146:21
    at file:///Users/m.hagemeister/dev/github/wmr/src/plugins/npm-plugin/utils.js:58:10
    at resolveId (file:///Users/m.hagemeister/dev/github/wmr/src/wmr-middleware.js:307:19)
    at async file:///Users/m.hagemeister/dev/github/wmr/src/lib/transform-imports.js:131:32
    at async Promise.all (index 3)
    at async transformImports (file:///Users/m.hagemeister/dev/github/wmr/src/lib/transform-imports.js:129:2)
    at async js (file:///Users/m.hagemeister/dev/github/wmr/src/wmr-middleware.js:267:10)
    at async Array.<anonymous> (file:///Users/m.hagemeister/dev/github/wmr/src/wmr-middleware.js:174:19)

Process shim throwing error when the word "import" is present

// import <-- enough to trigger the regex
document.getElementById("out").textContent = process.env.NODE_ENV

Error message:

500 ./index.js - Error: Invalid specifier: process.js
        at file:///Users/m.hagemeister/dev/github/wmr/src/plugins/npm-plugin/index.js:146:21
        at file:///Users/m.hagemeister/dev/github/wmr/src/plugins/npm-plugin/utils.js:58:10
        at resolveId (file:///Users/m.hagemeister/dev/github/wmr/src/wmr-middleware.js:307:19)
        at async file:///Users/m.hagemeister/dev/github/wmr/src/lib/transform-imports.js:131:32
        at async Promise.all (index 0)
        at async transformImports (file:///Users/m.hagemeister/dev/github/wmr/src/lib/transform-imports.js:129:2)
        at async js (file:///Users/m.hagemeister/dev/github/wmr/src/wmr-middleware.js:267:10)
        at async Array.<anonymous> (file:///Users/m.hagemeister/dev/github/wmr/src/wmr-middleware.js:174:19)

Exception when user is offline

I'm currently commuting by train and the wifi is spotty at best. Got the following exception from wmr:

    events.js:291
          throw er; // Unhandled 'error' event
          ^
    
    Error: getaddrinfo ENOTFOUND registry.npmjs.org
        at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:66:26)
    Emitted 'error' event on ClientRequest instance at:
        at TLSSocket.socketErrorListener (_http_client.js:469:9)
        at TLSSocket.emit (events.js:314:20)
        at emitErrorNT (internal/streams/destroy.js:100:8)
        at emitErrorCloseNT (internal/streams/destroy.js:68:3)
        at processTicksAndRejections (internal/process/task_queues.js:80:21) {
      errno: -3008,
      code: 'ENOTFOUND',
      syscall: 'getaddrinfo',
      hostname: 'registry.npmjs.org'
    }

Imo we should handle the offline scenario gracefully. Maybe just print a warning that the user appears to be offline and that automatic npm downloads are therefore disabled.

Action required: Greenkeeper could not be activated 🚨


🚨 Reminder! Less than one month left to migrate your repositories over to Snyk before Greenkeeper says goodbye on June 3rd! πŸ’œ πŸššπŸ’¨ πŸ’š

Find out how to migrate to Snyk at greenkeeper.io


🚨 You need to enable Continuous Integration on Greenkeeper branches of this repository. 🚨

To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because it uses your CI build statuses to figure out when to notify you about breaking changes.

Since we didn’t receive a CI status on the greenkeeper/initial branch, it’s possible that you don’t have CI set up yet.
We recommend using:

If you have already set up a CI for this repository, you might need to check how it’s configured. Make sure it is set to run on all new branches. If you don’t want it to run on absolutely every branch, you can whitelist branches starting with greenkeeper/.

Once you have installed and configured CI on this repository correctly, you’ll need to re-trigger Greenkeeper’s initial pull request. To do this, please click the 'fix repo' button on account.greenkeeper.io.

Default to serving 200.html / index.html

Currently, requesting a path that resolves to a directory returns [object Object]. Instead, any navigation requests that are not resolved by wmr-middleware.js or sirv(public) should fall back to serving a 200.html file in the public directory. If that file doesn't exist, they should fall back to serving index.html.

Firefox: HMR not working with CSS-Modules

In Firefox HMR doesn't seem to work. Upon changing a file the page doesn't show the new styles. Weirdly this only occurs with CSS-Modules. Can't reproduce that with a normal CSS file.

Steps to reproduce

  1. Run demo app
  2. Go to About page in Firefox
  3. Change background color to another value in style.module.css

Expected Result

The background color should be updated to the new value

Actual Result

Nothing seems to change.

Trying to understand the npm-plugin

I've now spent a couple of hours going through the code of our npm-plugin, but still don't have a good grasp on the control flow. On a high level it's easy to understand what it's supposed to do, but the devil is in the details. So this issue is more of notebook for me on my journey to figure out what is going on.

Noticed that we have multiple caches that do the same thing: Resolve a module request to the actual file content. The shape of those are:

Map<ModuleSpecifier, Map<FilePath, FileContent>>

Those caches only differ in that the presence of a module specifier signals that an attempt to fetch it from the network/local npm cache was already made.

There are multiple resolution strategies in the current code:

  1. Check in memory cache
  2. Check if it is available in local node_modules folder
  3. Check if the tarball is available in the global npm/yarn cache folder (not implemented yet)
  4. Retrieve tarball from registry
  5. Bundle retrieved package and store the results in the cache of 1.

The tricky bit is that we receive cache requests in parallel. The code currently does account for that via a whens abstraction. It's meant to only be resolved once an item has been resolved.

To ensure that parallel requests don't trigger multiple fetches of the same tarball the code uses a memo function to return the same Promise object as the initiator to await on.

Improvement ideas

  • Ideally we only have one file cache and different strategies to put stuff into the cache. This hopefully simplifies retrieval access and makes the caching logic more explicit.
  • Rename whenFiles to something more descriptive like loadFile. Same for whens which could be named pending or something.
  • Is the whenFiles abstraction necessary or could we reuse a plain Promise with multiple listeners here?

Unable to import scoped packages like `@urql/core`

Error:

500 /@npm/@urql/core - Error: Unknown package export ./dist/2b2328af.mjs in @urql/core.

{
  ".": {
    "import": "./dist/urql-core.mjs",
    "require": "./dist/urql-core.js",
    "types": "./dist/types/index.d.ts",
    "source": "./src/index.ts"
  },
  "./package.json": "./package.json",
  "./internal": {
    "import": "./dist/urql-core-internal.mjs",
    "require": "./dist/urql-core-internal.js",
    "types": "./dist/types/internal/index.d.ts",
    "source": "./src/internal/index.ts"
  }
}
    at resolveModule (/my-project/wmr.cjs:51142:10)
    at async Object.resolveId (/my-project/wmr.cjs:51335:23)
    at async resolveId (/my-project/wmr.cjs:17689:26)
    at async ModuleLoader.resolveId (/my-project/wmr.cjs:18050:15)
    at async /my-project/wmr.cjs:18187:38
    at async Promise.all (index 6)
    at async ModuleLoader.fetchStaticDependencies (/my-project/wmr.cjs:18185:34)
    at async Promise.all (index 0)
    at async ModuleLoader.fetchModule (/my-project/wmr.cjs:18162:9)
    at async Promise.all (index 0)

Looking at the files on disk in node_modules it seems like wmr resolves @urql/core to urql/core. The error message itself seems to come from wmr checking all files in dist/ and if they match an entry in the export map. That 1:1 assumption doesn't hold true here as urql uses a chunked output.

Invalid import path for relative alias declarations

With a project structure like this:

/my-project
  foo.js
  index.js
  index.html

and an alias declaration like this:

{
  "alias": {
    "foobar": "./foo.js"
  }
}

and an index.js like this:

import * as foobar from "foobar";
console.log(foobar);

WMR adds the cwd path twice. The final resolved path will be /path/to/my-project/path/to/my-project/foo.js instead of the expected /path/to/my-project/foo.js

This only happens when the alias starts with a .. If I declare it as an absolute posix path it is resolved correctly:

{
  "alias": {
    "foobar": "/foo.js"
  }
}

process.js import not working

Looks the fix for #113 lead to every process variable being inlined. If there is a \0builtins:process.js path present, it will be rewritten to the cwd regardless. This leads to invalid paths like \0builtins:/../process.js, which ends up in the browser as /process.js.

Get rid of devcert

Our --http2 mode currently relies on an npm module called devcert, which installs a root CA on the users machine. That's not cool from a security perspective.

Unable to resolve source maps

Ran into this error with a project at work (file locations are from unminified wmr build):

Error bundling react-toastify/dist/ReactToastify.css.map:  Error: Unexpected token (Note that you need plugins to import files that are not JavaScript)
    at error$1 (/my-project/wmr.cjs:5501:30)
    at Module.error (/my-project/wmr.cjs:9933:16)
    at tryParse (/my-project/wmr.cjs:9847:23)
    at Module.setSource (/my-project/wmr.cjs:10232:30)
    at ModuleLoader.addModuleSource (/my-project/wmr.cjs:18107:20)
    at async ModuleLoader.fetchModule (/my-project/wmr.cjs:18161:9)
    at async Promise.all (index 0) {
  code: 'PARSE_ERROR',
  parserError: SyntaxError: Unexpected token (1:10)
      at Object.pp$4.raise (/my-project/wmr.cjs:15429:13)
      at Object.pp.unexpected (/my-project/wmr.cjs:13200:8)
      at Object.pp.semicolon (/my-project/wmr.cjs:13177:64)
      at Object.pp$1.parseExpressionStatement (/my-project/wmr.cjs:13656:8)
      at Object.pp$1.parseStatement (/my-project/wmr.cjs:13391:24)
      at Object.pp$1.parseBlock (/my-project/wmr.cjs:13672:21)
      at Object.pp$1.parseStatement (/my-project/wmr.cjs:13356:34)
      at Object.pp$1.parseTopLevel (/my-project/wmr.cjs:13257:21)
      at Object.parse (/my-project/wmr.cjs:13057:15)
      at Function.parse (/my-project/wmr.cjs:13080:35) {
    pos: 10,
    loc: Position { line: 1, column: 10 },
    raisedAt: 11
  },
  id: '\bnpm/[email protected]/dist/ReactToastify.css.map',
  pos: 10,
  loc: {
    file: '\bnpm/[email protected]/dist/ReactToastify.css.map',
    line: 1,
    column: 10
  },
  frame: '1: {"version":3,"sources":["ReactToastify.css"],"names":[],"mappings":"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","file":"ReactToastify.css","sourcesContent":[".Toastify__toast-container {\\n  z-index: 9999;\\n  -webkit-transform: translate3d(0, 0, 9999px);\\n  position: fixed;\\n  padding: 4px;\\n  width: 320px;\\n  box-sizing: border-box;\\n  color: #fff; }\\n  .Toastify__toast-container--top-left {\\n    top: 1em;\\n    left: 1em; }\\n  .Toastify__toast-container--top-center {\\n    top: 1em;\\n    left: 50%;\\n    margin-left: -160px; }\\n  .Toastify__toast-container--top-right {\\n    top: 1em;\\n    right: 1em; }\\n  .Toastify__toast-container--bottom-left {\\n    bottom: 1em;\\n    left: 1em; }\\n  .Toastify__toast-container--bottom-center {\\n    bottom: 1em;\\n    left: 50%;\\n    margin-left: -160px; }\\n  .Toastify__toast-container--bottom-right {\\n    bottom: 1em;\\n    right: 1em; }\\n\\n@media only screen and (max-width: 480px) {\\n  .Toastify__toast-container {\\n    width: 100vw;\\n    padding: 0;\\n    left: 0;\\n    margin: 0; }\\n    .Toastify__toast-container--top-left, .Toastify__toast-container--top-center, .Toastify__toast-container--top-right {\\n      top: 0; }\\n    .Toastify__toast-container--bottom-left, .Toastify__toast-container--bottom-center, .Toastify__toast-container--bottom-right {\\n      bottom: 0; }\\n    .Toastify__toast-container--rtl {\\n      right: 0;\\n      left: initial; } }\\n\\n.Toastify__toast {\\n  position: relative;\\n  min-height: 64px;\\n  box-sizing: border-box;\\n  margin-bottom: 1rem;\\n  padding: 8px;\\n  border-radius: 1px;\\n  box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 0.1), 0 2px 15px 0 rgba(0, 0, 0, 0.05);\\n  display: flex;\\n  justify-content: space-between;\\n  max-height: 800px;\\n  overflow: hidden;\\n  font-family: sans-serif;\\n  cursor: pointer;\\n  direction: ltr; }\\n  .Toastify__toast--rtl {\\n    direction: rtl; }\\n  .Toastify__toast--default {\\n    background: #fff;\\n    color: #aaa; }\\n  .Toastify__toast--info {\\n    background: #3498db; }\\n  .Toastify__toast--success {\\n    background: #07bc0c; }\\n  .Toastify__toast--warning {\\n    background: #f1c40f; }\\n  .Toastify__toast--error {\\n    background: #e74c3c; }\\n  .Toastify__toast-body {\\n    margin: auto 0;\\n    flex: 1; }\\n\\n@media only screen and (max-width: 480px) {\\n  .Toastify__toast {\\n    margin-bottom: 0; } }\\n\\n.Toastify__close-button {\\n  color: #fff;\\n  font-weight: bold;\\n  font-size: 14px;\\n  background: transparent;\\n  outline: none;\\n  border: none;\\n  padding: 0;\\n  cursor: pointer;\\n  opacity: 0.7;\\n  transition: 0.3s ease;\\n  align-self: flex-start; }\\n  .Toastify__close-button--default {\\n    color: #000;\\n    opacity: 0.3; }\\n  .Toastify__close-button:hover, .Toastify__close-button:focus {\\n    opacity: 1; }\\n\\n@keyframes Toastify__trackProgress {\\n  0% {\\n    transform: scaleX(1); }\\n  100% {\\n    transform: scaleX(0); } }\\n\\n.Toastify__progress-bar {\\n  position: absolute;\\n  bottom: 0;\\n  left: 0;\\n  width: 100%;\\n  height: 5px;\\n  z-index: 9999;\\n  opacity: 0.7;\\n  background-color: rgba(255, 255, 255, 0.7);\\n  transform-origin: left; }\\n  .Toastify__progress-bar--animated {\\n    animation: Toastify__trackProgress linear 1 forwards; }\\n  .Toastify__progress-bar--controlled {\\n    transition: transform .2s; }\\n  .Toastify__progress-bar--rtl {\\n    right: 0;\\n    left: initial;\\n    transform-origin: right; }\\n  .Toastify__progress-bar--default {\\n    background: linear-gradient(to right, #4cd964, #5ac8fa, #007aff, #34aadc, #5856d6, #ff2d55); }\\n\\n@keyframes Toastify__bounceInRight {\\n  from,\\n  60%,\\n  75%,\\n  90%,\\n  to {\\n    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); }\\n  from {\\n    opacity: 0;\\n    transform: translate3d(3000px, 0, 0); }\\n  60% {\\n    opacity: 1;\\n    transform: translate3d(-25px, 0, 0); }\\n  75% {\\n    transform: translate3d(10px, 0, 0); }\\n  90% {\\n    transform: translate3d(-5px, 0, 0); }\\n  to {\\n    transform: none; } }\\n\\n@keyframes Toastify__bounceOutRight {\\n  20% {\\n    opacity: 1;\\n    transform: translate3d(-20px, 0, 0); }\\n  to {\\n    opacity: 0;\\n    transform: translate3d(2000px, 0, 0); } }\\n\\n@keyframes Toastify__bounceInLeft {\\n  from,\\n  60%,\\n  75%,\\n  90%,\\n  to {\\n    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); }\\n  0% {\\n    opacity: 0;\\n    transform: translate3d(-3000px, 0, 0); }\\n  60% {\\n    opacity: 1;\\n    transform: translate3d(25px, 0, 0); }\\n  75% {\\n    transform: translate3d(-10px, 0, 0); }\\n  90% {\\n    transform: translate3d(5px, 0, 0); }\\n  to {\\n    transform: none; } }\\n\\n@keyframes Toastify__bounceOutLeft {\\n  20% {\\n    opacity: 1;\\n    transform: translate3d(20px, 0, 0); }\\n  to {\\n    opacity: 0;\\n    transform: translate3d(-2000px, 0, 0); } }\\n\\n@keyframes Toastify__bounceInUp {\\n  from,\\n  60%,\\n  75%,\\n  90%,\\n  to {\\n    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); }\\n  from {\\n    opacity: 0;\\n    transform: translate3d(0, 3000px, 0); }\\n  60% {\\n    opacity: 1;\\n    transform: translate3d(0, -20px, 0); }\\n  75% {\\n    transform: translate3d(0, 10px, 0); }\\n  90% {\\n    transform: translate3d(0, -5px, 0); }\\n  to {\\n    transform: translate3d(0, 0, 0); } }\\n\\n@keyframes Toastify__bounceOutUp {\\n  20% {\\n    transform: translate3d(0, -10px, 0); }\\n  40%,\\n  45% {\\n    opacity: 1;\\n    transform: translate3d(0, 20px, 0); }\\n  to {\\n    opacity: 0;\\n    transform: translate3d(0, -2000px, 0); } }\\n\\n@keyframes Toastify__bounceInDown {\\n  from,\\n  60%,\\n  75%,\\n  90%,\\n  to {\\n    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); }\\n  0% {\\n    opacity: 0;\\n    transform: translate3d(0, -3000px, 0); }\\n  60% {\\n    opacity: 1;\\n    transform: translate3d(0, 25px, 0); }\\n  75% {\\n    transform: translate3d(0, -10px, 0); }\\n  90% {\\n    transform: translate3d(0, 5px, 0); }\\n  to {\\n    transform: none; } }\\n\\n@keyframes Toastify__bounceOutDown {\\n  20% {\\n    transform: translate3d(0, 10px, 0); }\\n  40%,\\n  45% {\\n    opacity: 1;\\n    transform: translate3d(0, -20px, 0); }\\n  to {\\n    opacity: 0;\\n    transform: translate3d(0, 2000px, 0); } }\\n\\n.Toastify__bounce-enter--top-left, .Toastify__bounce-enter--bottom-left {\\n  animation-name: Toastify__bounceInLeft; }\\n\\n.Toastify__bounce-enter--top-right, .Toastify__bounce-enter--bottom-right {\\n  animation-name: Toastify__bounceInRight; }\\n\\n.Toastify__bounce-enter--top-center {\\n  animation-name: Toastify__bounceInDown; }\\n\\n.Toastify__bounce-enter--bottom-center {\\n  animation-name: Toastify__bounceInUp; }\\n\\n.Toastify__bounce-exit--top-left, .Toastify__bounce-exit--bottom-left {\\n  animation-name: Toastify__bounceOutLeft; }\\n\\n.Toastify__bounce-exit--top-right, .Toastify__bounce-exit--bottom-right {\\n  animation-name: Toastify__bounceOutRight; }\\n\\n.Toastify__bounce-exit--top-center {\\n  animation-name: Toastify__bounceOutUp; }\\n\\n.Toastify__bounce-exit--bottom-center {\\n  animation-name: Toastify__bounceOutDown; }\\n\\n@keyframes Toastify__zoomIn {\\n  from {\\n    opacity: 0;\\n    transform: scale3d(0.3, 0.3, 0.3); }\\n  50% {\\n    opacity: 1; } }\\n\\n@keyframes Toastify__zoomOut {\\n  from {\\n    opacity: 1; }\\n  50% {\\n    opacity: 0;\\n    transform: scale3d(0.3, 0.3, 0.3); }\\n  to {\\n    opacity: 0; } }\\n\\n.Toastify__zoom-enter {\\n  animation-name: Toastify__zoomIn; }\\n\\n.Toastify__zoom-exit {\\n  animation-name: Toastify__zoomOut; }\\n\\n@keyframes Toastify__flipIn {\\n  from {\\n    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\\n    animation-timing-function: ease-in;\\n    opacity: 0; }\\n  40% {\\n    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\\n    animation-timing-function: ease-in; }\\n  60% {\\n    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);\\n    opacity: 1; }\\n  80% {\\n    transform: perspective(400px) rotate3d(1, 0, 0, -5deg); }\\n  to {\\n    transform: perspective(400px); } }\\n\\n@keyframes Toastify__flipOut {\\n  from {\\n    transform: perspective(400px); }\\n  30% {\\n    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);\\n    opacity: 1; }\\n  to {\\n    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);\\n    opacity: 0; } }\\n\\n.Toastify__flip-enter {\\n  animation-name: Toastify__flipIn; }\\n\\n.Toastify__flip-exit {\\n  animation-name: Toastify__flipOut; }\\n\\n@keyframes Toastify__slideInRight {\\n  from {\\n    transform: translate3d(110%, 0, 0);\\n    visibility: visible; }\\n  to {\\n    transform: translate3d(0, 0, 0); } }\\n\\n@keyframes Toastify__slideInLeft {\\n  from {\\n    transform: translate3d(-110%, 0, 0);\\n    visibility: visible; }\\n  to {\\n    transform: translate3d(0, 0, 0); } }\\n\\n@keyframes Toastify__slideInUp {\\n  from {\\n    transform: translate3d(0, 110%, 0);\\n    visibility: visible; }\\n  to {\\n    transform: translate3d(0, 0, 0); } }\\n\\n@keyframes Toastify__slideInDown {\\n  from {\\n    transform: translate3d(0, -110%, 0);\\n    visibility: visible; }\\n  to {\\n    transform: translate3d(0, 0, 0); } }\\n\\n@keyframes Toastify__slideOutRight {\\n  from {\\n    transform: translate3d(0, 0, 0); }\\n  to {\\n    visibility: hidden;\\n    transform: translate3d(110%, 0, 0); } }\\n\\n@keyframes Toastify__slideOutLeft {\\n  from {\\n    transform: translate3d(0, 0, 0); }\\n  to {\\n    visibility: hidden;\\n    transform: translate3d(-110%, 0, 0); } }\\n\\n@keyframes Toastify__slideOutDown {\\n  from {\\n    transform: translate3d(0, 0, 0); }\\n  to {\\n    visibility: hidden;\\n    transform: translate3d(0, 500px, 0); } }\\n\\n@keyframes Toastify__slideOutUp {\\n  from {\\n    transform: translate3d(0, 0, 0); }\\n  to {\\n    visibility: hidden;\\n    transform: translate3d(0, -500px, 0); } }\\n\\n.Toastify__slide-enter--top-left, .Toastify__slide-enter--bottom-left {\\n  animation-name: Toastify__slideInLeft; }\\n\\n.Toastify__slide-enter--top-right, .Toastify__slide-enter--bottom-right {\\n  animation-name: Toastify__slideInRight; }\\n\\n.Toastify__slide-enter--top-center {\\n  animation-name: Toastify__slideInDown; }\\n\\n.Toastify__slide-enter--bottom-center {\\n  animation-name: Toastify__slideInUp; }\\n\\n.Toastify__slide-exit--top-left, .Toastify__slide-exit--bottom-left {\\n  animation-name: Toastify__slideOutLeft; }\\n\\n.Toastify__slide-exit--top-right, .Toastify__slide-exit--bottom-right {\\n  animation-name: Toastify__slideOutRight; }\\n\\n.Toastify__slide-exit--top-center {\\n  animation-name: Toastify__slideOutUp; }\\n\\n.Toastify__slide-exit--bottom-center {\\n  animation-name: Toastify__slideOutDown; }\\n"]}\n' +
    '             ^',
  watchFiles: [
    '/my-project/package.json',
    '\bnpm/[email protected]/dist/ReactToastify.css.map'
  ]
}

Just checked, the file on disk is correct, but wmr seems to end up with an invalid one. Afaik the 1: suffix should not be there.

1: {"version":3, ...}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.