diff --git a/package.json b/package.json index 320c1e6..6da113b 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "__scripts:comment:bundle2": "todo: could improve it by deleting everything except _snowpack - something like rm -rfvi !('build/_snowpack'). be careful not to delete anything above build/", "scripts": { "bundle": "npm run build && rm -f build/index.js", - "watch": "snowpack build --watch --hmr", + "watch": "snowpack dev", "build": "snowpack build" } } diff --git a/plugin.php b/plugin.php index 4a25c7f..045b003 100644 --- a/plugin.php +++ b/plugin.php @@ -83,6 +83,22 @@ function get_serve_folder() { return $folder; } +// Note: This is only necessary to demonstrate different approaches. In real-world applications you'd either +// commit to running the watch task or not, or at least the application code wouldn't care which. +function running_watch_task() { + static $running = null; // Cache the result so it's not run more than once per page load. + + if ( null === $running ) { + $null = null; + $running = is_resource( @fsockopen( 'localhost', '8081', $null, $null, 1 ) ); + + if ( ! $running ) { + error_clear_last(); // Prevent the `body.php-error` CSS class from being added. + } + } + + return $running; +} // register/enqueue scripts & styles add_action( 'admin_enqueue_scripts', function( $hook_suffix ) { @@ -95,6 +111,13 @@ function get_serve_folder() { // need to add wp-polyfill as a dependency? maybe for api-fetch & other stuff // it's added automatically? + if ( running_watch_task() ) { + $script_url = 'http://localhost:8081/index.js'; + // make url/port DRY w/ websocket below + } else { + $script_url = plugins_url( "$folder/index.js", __FILE__ ); + } + if ( 'source' === $folder ) { // Don't need HTM in production, because Babel transpiles it away. $dependencies[] = 'htm'; @@ -106,7 +129,8 @@ function get_serve_folder() { wp_enqueue_script( 'no-build-tools-no-problems', - plugins_url( "$folder/index.js", __FILE__ ), + $script_url, + /// rename to more specific $dependencies, filemtime( __DIR__ . "/$folder/index.js" ), @@ -164,27 +188,19 @@ function get_serve_folder() { - - - build step for your production files. You'll still be able to develop locally without any `watch` tooling, though. 1. `npm install` 1. `npm run build` @@ -42,15 +42,15 @@ There are two ways to import dependencies, depending on your needs and preferenc 1. **Locally bundled packages:** You can also bundle packages locally if you prefer, but still much faster and more conveniently than traditional approaches. - [Snowpack](https://snowpack.dev) is used to generate bundles 10x faster than Webpack, and only when needed. You don't need to run a `watch` task, just `npm run bundle` when you add/remove a dependency. It still does tree-shaking, will automatically up-convert CommonJS modules to ESM, and has a much more ergonomic approach to package locking. + [Snowpack](https://snowpack.dev) is used to generate bundles 10x faster than Webpack, and only when needed. You don't need to run the `watch` task, just `npm run bundle` when you add/remove a dependency. It still does tree-shaking, will automatically up-convert CommonJS modules to ESM, and has a much more ergonomic approach to package locking. ### Optional Tooling for Improved Developer Experience npm run watch will add additional features, but it's not required. -* Today: Live reloading and PostCSS. -* Future: Hot Module Reloading, React Fast Refresh. +* Today: Hot Module Reloading and PostCSS. +* Future: React Fast Refresh. ### Results diff --git a/snowpack.config.js b/snowpack.config.js index 7de3c38..ab0613a 100644 --- a/snowpack.config.js +++ b/snowpack.config.js @@ -43,8 +43,10 @@ module.exports = { source: 'remote', }, - // Not using their localhost server because need everything to run inside WordPress/PHP context. - //devOptions: {}, + devOptions: { + port: 8081, // Avoid conflicts with proxies on 8080. + open: 'none', // Devs will still visit the WP/PHP server URL in their browser. + }, buildOptions: { metaUrlPath : 'vendor', diff --git a/source/baseline/latest-posts.js b/source/baseline/latest-posts.js index e715f0a..f9332c9 100644 --- a/source/baseline/latest-posts.js +++ b/source/baseline/latest-posts.js @@ -49,7 +49,7 @@ export class LatestPostsCard extends Component { render = () => { const { loaded, loading, posts } = this.state; - const componentUrl = getBaseUrl() + '/baseline/'; + const componentUrl = getBaseUrl() + '/baseline'; return html` <${Fragment}> diff --git a/source/developer-experience/developer-experience.js b/source/developer-experience/developer-experience.js index 9d76822..59f6386 100644 --- a/source/developer-experience/developer-experience.js +++ b/source/developer-experience/developer-experience.js @@ -12,7 +12,8 @@ import { getLoadPath, getBaseUrl } from '../utilities.js'; export function DeveloperExperience() { const loadPath = getLoadPath(); - const componentUrl = getBaseUrl() + '/developer-experience/'; + const watching = 'object' === typeof import.meta?.hot; + const componentUrl = getBaseUrl() + '/developer-experience'; return html` <${Fragment}> @@ -43,11 +44,23 @@ export function DeveloperExperience() {

- ${ 'source' === loadPath && html` + + ${ 'source' === loadPath && ! watching && html` <${ Notice } status="info" isDismissible=${ false } >

- You're running from source/, so the nested styles won't apply, and the image below will be blurred. - npm run build and refresh to load the build/ files, where PostCSS will transform the nested styles to vanilla CSS and un-blur the image. + You're running from source/ without the watch task, so the nested styles won't apply, and the image below will be blurred. + + npm run watch or npm run build and then refresh. That will cause PostCSS to transform the nested styles to vanilla CSS, and un-blur the image. +

+ + ` } + + ${ watching && html` + <${ Notice } status="info" isDismissible=${ false } > +

+ You're running the watch task, so the nested styles will apply, and the image below will not be blurred. + + Control-C in your terminal to cancel the watch task, then refresh to load the unprocessed files. When you do, the image will be blurred.

` } diff --git a/source/index.js b/source/index.js index 461232e..2cb72b7 100644 --- a/source/index.js +++ b/source/index.js @@ -5,6 +5,10 @@ const html = wp.html; const { Fragment } = wp.element; const { renderLoadingContainer } = wp.utils; +// This is required for HMR to work, because we're using WP to serve the HTML instead of Snowpack's localhost server. +if ( import.meta.hot ) { + import.meta.hot.accept(); +} /** * Internal dependencies diff --git a/source/local-bundling/passphrase-generator.js b/source/local-bundling/passphrase-generator.js index 994c224..a00583b 100644 --- a/source/local-bundling/passphrase-generator.js +++ b/source/local-bundling/passphrase-generator.js @@ -15,6 +15,7 @@ const html = wp.html; */ //import { v4 as uuidv4 } from 'uuid'; // Native ESM //import { argon2id } from 'hash-wasm'; // Native ESM +// maybe change these to dynamic imports, so could flip the card on/off automatically based on import.meta.hot || build === path? // todo convert these to import maps too, but need to wait until https://github.com/snowpackjs/snowpack/discussions/2548 is released // then check if can destructure specific functions from any of these, since snowpack will be is bundling @@ -140,7 +141,7 @@ export class PassphraseGenerator extends Component { const dependenciesAvailable = 'function' === typeof uuidv4 && 'function' === typeof argon2id; // todo add others when they're loaded from local bundle - const componentUrl = getBaseUrl() + '/local-bundling/'; + const componentUrl = getBaseUrl() + '/local-bundling'; return html` <${Fragment}> diff --git a/source/utilities.js b/source/utilities.js index dac32ff..ce742ee 100644 --- a/source/utilities.js +++ b/source/utilities.js @@ -2,7 +2,7 @@ export function getBaseUrl() { const scriptUrl = document.getElementById( 'no-build-tools-no-problems-js' ).getAttribute( 'src' ); - return scriptUrl.substring( 0, scriptUrl.lastIndexOf( '/' ) ) + '/'; + return scriptUrl.substring( 0, scriptUrl.lastIndexOf( '/' ) ); } // Returns 'build' if loaded from `build/` folder, 'source' otherwise