Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): add pwa to plugins #746

Merged
merged 8 commits into from
Sep 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions adapter/src/components/ServerVersionProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export const ServerVersionProvider = ({
setState({ loading: false, systemInfo })
})
.catch((e) => {
// Todo: If this is a network error, the app cannot load -- handle that gracefully here
// if (e === 'Network error') { ... }
setState({ loading: false, error: e })
})

Expand Down
2 changes: 1 addition & 1 deletion adapter/src/utils/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const request = (url, options) => {
})
.then((response) => {
if (response.status !== 200) {
reject('Request failed', response.statusText)
reject('Request failed ' + response.statusText)
return
}
try {
Expand Down
3 changes: 2 additions & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@
"terser-webpack-plugin": "^5.3.1",
"webpack": "^5.41.1",
"webpack-dev-server": "^4.7.4",
"workbox-build": "^6.1.5"
"workbox-build": "^6.1.5",
"workbox-webpack-plugin": "^6.5.4"
},
"bin": {
"d2-app-scripts": "./bin/d2-app-scripts"
Expand Down
5 changes: 4 additions & 1 deletion cli/src/commands/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,14 @@ const handler = async ({
await shell.build()

if (config.entryPoints.plugin) {
reporter.info('Building plugin...')
await plugin.build()
}

if (config.pwa.enabled) {
reporter.info('Injecting precache manifest...')
reporter.info(
'Injecting supplementary precache manifest...'
)
await injectPrecacheManifest(paths, config)
}
} else {
Expand Down
1 change: 1 addition & 0 deletions cli/src/lib/parseConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const validateConfig = (config) => {
process.exit(1)
}
})
// Todo: make sure apps with plugins have `pwa_enabled: true` in the config
return true
}

Expand Down
8 changes: 6 additions & 2 deletions cli/src/lib/plugin/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ const { reporter } = require('@dhis2/cli-helpers-engine')
const webpack = require('webpack')
const webpackConfigFactory = require('./webpack.config')

module.exports = async ({ paths }) => {
module.exports = async ({ config, paths }) => {
reporter.debug('Building plugin...')

const webpackConfig = webpackConfigFactory({ env: 'production', paths })
const webpackConfig = webpackConfigFactory({
env: 'production',
config,
paths,
})
const compiler = webpack(webpackConfig)
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
Expand Down
9 changes: 7 additions & 2 deletions cli/src/lib/plugin/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ const webpack = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
const webpackConfigFactory = require('./webpack.config')

module.exports = async ({ port, paths }) => {
const webpackConfig = webpackConfigFactory({ env: 'production', paths })
module.exports = async ({ port, config, paths }) => {
const webpackConfig = webpackConfigFactory({
// todo: change to development, but this creates a compilation error
env: 'production',
config,
paths,
})
const compiler = webpack(webpackConfig)

const host = process.env.HOST || 'localhost'
Expand Down
49 changes: 43 additions & 6 deletions cli/src/lib/plugin/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent')
const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath')
const TerserPlugin = require('terser-webpack-plugin')
const webpack = require('webpack')
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
const makeBabelConfig = require('../../../config/makeBabelConfig')
const { getPWAEnvVars } = require('../pwa')
const getShellEnv = require('../shell/env')

const babelWebpackConfig = {
Expand All @@ -21,7 +23,7 @@ const babelWebpackConfig = {
const cssRegex = /\.css$/
const cssModuleRegex = /\.module\.css$/

module.exports = ({ env: webpackEnv, paths }) => {
module.exports = ({ env: webpackEnv, config, paths }) => {
const isProduction = webpackEnv === 'production'
const isDevelopment = !isProduction

Expand All @@ -31,7 +33,12 @@ module.exports = ({ env: webpackEnv, paths }) => {
process.env.PUBLIC_URL
)

const shellEnv = getShellEnv({ plugin: 'true' })
const shellEnv = getShellEnv({
plugin: 'true',
name: config.title,
// todo: need to make sure PWA is enabled for plugins
...getPWAEnvVars(config),
})

// "style" loader turns CSS into JS modules that inject <style> tags.
// "css" loader resolves paths in CSS and adds assets as dependencies.
Expand Down Expand Up @@ -99,6 +106,10 @@ module.exports = ({ env: webpackEnv, paths }) => {
chunkFilename: isProduction
? 'static/js/plugin-[name].[contenthash:8].chunk.js'
: 'static/js/plugin-[name].chunk.js',
// ! dhis2: this at least gets fonts to match the CRA build,
// but is re-outputting them
assetModuleFilename: 'static/media/[name].[hash][ext]',
// TODO: investigate dev source maps here (devtoolModuleFilenameTemplate)
},
optimization: {
minimize: isProduction,
Expand Down Expand Up @@ -180,6 +191,27 @@ module.exports = ({ env: webpackEnv, paths }) => {
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/,
}),
// dhis2: Inject plugin static assets to the existing SW's precache manifest
process.env.NODE_ENV === 'production' &&
new WorkboxWebpackPlugin.InjectManifest({
swSrc: paths.shellBuildServiceWorker,
injectionPoint: 'self.__WB_PLUGIN_MANIFEST',
// Skip compiling the SW, which happens in the app build step
compileSrc: false,
dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,
exclude: [
/\.map$/,
/asset-manifest\.json$/,
/LICENSE/,
// TODO dhis2: locales are weird in the plugin build -
// Ignore them in precache manifest for now
/moment-locales/,
],
// Bump up the default maximum size (2mb) that's precached,
// to make lazy-loading failure scenarios less likely.
// See https://github.com/cra-template/pwa/issues/13#issuecomment-722667270
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
}),
].filter(Boolean),
module: {
rules: [
Expand Down Expand Up @@ -223,6 +255,9 @@ module.exports = ({ env: webpackEnv, paths }) => {
use: getStyleLoaders({
importLoaders: 1,
sourceMap: true,
modules: {
mode: 'icss',
},
}),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
Expand All @@ -236,6 +271,7 @@ module.exports = ({ env: webpackEnv, paths }) => {
importLoaders: 1,
sourceMap: true,
modules: {
mode: 'local',
getLocalIdent: getCSSModuleLocalIdent,
},
}),
Expand All @@ -246,23 +282,24 @@ module.exports = ({ env: webpackEnv, paths }) => {
// This loader doesn't use a "test" so it will catch all modules
// that fall through the other loaders.
{
loader: require.resolve('file-loader'),
// Exclude `js` files to keep "css" loader working as it injects
// its runtime that would otherwise be processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.
exclude: [
/^$/,
/\.(js|mjs|jsx|ts|tsx)$/,
/\.html$/,
/\.json$/,
],
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
type: 'asset/resource',
},
],
},
],
},
// Saves some chunk size logging
performance: false,
// stats: 'verbose',
}
}
8 changes: 7 additions & 1 deletion pwa/src/service-worker/service-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ export function setUpServiceWorker() {
// Includes all built assets and index.html
const precacheManifest = self.__WB_MANIFEST || []

// Same thing for built plugin assets
const pluginPrecacheManifest = self.__WB_PLUGIN_MANIFEST || []
precacheAndRoute(pluginPrecacheManifest)

// todo: also do this routing for plugin.html
// Extract index.html from the manifest to precache, then route
// in a custom way
const indexHtmlManifestEntry = precacheManifest.find(({ url }) =>
Expand Down Expand Up @@ -128,7 +133,8 @@ export function setUpServiceWorker() {
// `additionalManifestEntries` option in d2.config.js; see the docs and
// 'injectPrecacheManifest.js' in the CLI package.
// '[]' fallback prevents an error when switching pwa enabled to disabled
precacheAndRoute(self.__WB_BUILD_MANIFEST || [])
const sharedBuildManifest = self.__WB_BUILD_MANIFEST || []
precacheAndRoute(sharedBuildManifest)
}

// Request handler during recording mode: ALL requests are cached
Expand Down
14 changes: 14 additions & 0 deletions pwa/src/service-worker/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import {
} from '../lib/sections-db.js'

const CACHE_KEEP_LIST = ['other-assets', 'app-shell']
const APP_ADAPTER_URL_PATTERNS = [
/\/api(\/\d+)?\/system\/info/, // from ServerVersionProvider
/\/api(\/\d+)?\/userSettings/, // useLocale
/\/api(\/\d+)?\/me\?fields=id$/, // useVerifyLatestUser
]

// '[]' Fallback prevents error when switching from pwa enabled to disabled
const APP_SHELL_URL_FILTER_PATTERNS = JSON.parse(
process.env
Expand Down Expand Up @@ -46,6 +52,14 @@ export function setUpKillSwitchServiceWorker() {
}

export function urlMeetsAppShellCachingCriteria(url) {
// Cache this request if it is important for the app adapter to load
const isAdapterRequest = APP_ADAPTER_URL_PATTERNS.some((pattern) =>
pattern.test(url.href)
)
if (isAdapterRequest) {
return true
}

// Don't cache if pwa.caching.omitExternalRequests in d2.config is true
if (
OMIT_EXTERNAL_REQUESTS_FROM_APP_SHELL &&
Expand Down
Loading