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

Remix PWA v5 Release Candidate #269

Merged
merged 32 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6fa72a5
Merge pull request #267 from remix-pwa/dev
ShafSpecs Sep 24, 2024
434ad79
chore: updated packages
ShafSpecs Sep 26, 2024
0626dff
chore(dev): testing out the feasibility of an SPA plugin
ShafSpecs Sep 29, 2024
f41696e
feat(dev): first v5 release candidate for `dev`
ShafSpecs Sep 30, 2024
e4e673d
chore: updated lock file
ShafSpecs Sep 30, 2024
1d778a7
feat(worker-runtime): runtime v5 release candidate
ShafSpecs Oct 1, 2024
7782c11
chore: re-configuring lock file
ShafSpecs Oct 1, 2024
9efb75b
test(dev): fixed failing test in `dev`
ShafSpecs Oct 1, 2024
ecfbdf0
chore: no comment
ShafSpecs Oct 1, 2024
fbcc598
chore: reversing lint mess
ShafSpecs Oct 1, 2024
86bd45c
feat(manifest): introduced `manifest` package
ShafSpecs Oct 1, 2024
f794305
fix(manifest): fixed `build` script
ShafSpecs Oct 3, 2024
294e9ed
chore(manifest): added ESLint config
ShafSpecs Oct 3, 2024
a1c0a3e
chore: removing lint error causes (for now)
ShafSpecs Oct 3, 2024
5fef667
chore: fixing more lint errors
ShafSpecs Oct 3, 2024
7273868
feat(dev): automatically disables auto-injection once the script is m…
ShafSpecs Oct 3, 2024
8326aaa
chore(manifest): removed default value for manifest `href`
ShafSpecs Oct 3, 2024
fea54a9
feat(manifest): added a JSON schema for web manifest
ShafSpecs Oct 3, 2024
89e3a90
feat(manifest): 🔥 defined exports - ready for shipping
ShafSpecs Oct 3, 2024
4af0952
chore: bumped `manifest` version
ShafSpecs Oct 3, 2024
32ba0a6
fix(manifest): fixed `rel` attribute for web manifests
ShafSpecs Oct 4, 2024
4ae2737
chore(manifest): added auto-complete to manifest `href`
ShafSpecs Oct 4, 2024
5fff516
chore: upated manifest
ShafSpecs Oct 5, 2024
7e9030b
feat(sw): moved `ManifestLink` + new component: `PWAScripts`
ShafSpecs Oct 5, 2024
2088a57
feat(sw): removed `useSWEffect` hook
ShafSpecs Oct 5, 2024
cf2f10c
feat(sw): removed manual sw loader - entirely
ShafSpecs Oct 5, 2024
e2c462e
feat(sw): new global hook + removed `SkipWaitHandler`
ShafSpecs Oct 5, 2024
cdf590e
chore(sw): prepping for `@remix-pwa/dev` addons feature
ShafSpecs Oct 5, 2024
e88aedb
feat(sw): `@remix-pwa/sw` v5 first release candidate
ShafSpecs Oct 5, 2024
6250443
test(sw): fixed failing message handler tests
ShafSpecs Oct 5, 2024
ab406bc
chore: upheaved sandbox even more
ShafSpecs Oct 5, 2024
752b57a
chore(client): refactored exports
ShafSpecs Oct 11, 2024
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
5,815 changes: 3,807 additions & 2,008 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"packages/dev",
"packages/eslint-config",
"packages/lint-staged-config",
"packages/manifest",
"packages/push",
"packages/sw",
"packages/sync",
Expand Down
2 changes: 1 addition & 1 deletion packages/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,6 @@ export {
export { useBadgeApi } from './hooks/useBadgeApi.js';
export { useBatteryManager } from './hooks/useBatteryManager.js';
export { useNetworkConnectivity } from './hooks/useNetworkConnectivity.js';
export { usePWAManager } from './hooks/usePWAManager.js';
export { usePermission } from './hooks/usePermission.js';
export type { PermissionName, PermissionState, PermissionStatus } from './hooks/usePermission.js';
export { usePWAManager } from './hooks/usePWAManager.js';
146 changes: 0 additions & 146 deletions packages/dev/index.ts
Original file line number Diff line number Diff line change
@@ -1,149 +1,3 @@
// Vite ✨
export { remixPWA } from './src/index.js';
export type { PWAOptions as PWAViteOptions } from './src/types.js';

export interface WebAppManifest {
/**
* The name member is a string that represents the name of the web application as it is usually
* displayed to the user (e.g., amongst a list of other applications, or as a label for an icon).
*/
name?: string;
/**
* The short_name member is a string that represents the name of the web application displayed to the
* user if there is not enough space to display name.
*/
short_name?: string;
/**
* The description member is a string that provides a description of the purpose of the web application.
*/
description?: string;
/**
* The icons member specifies an array of image objects that can serve as application icons for different
* contexts. For example, they can be used to represent the web application amongst a list of other applications,
* or to integrate the web application with an operating system's task switcher and/or system preferences.
*/
icons?: Array<{
src: string;
sizes?: string;
type?: string;
purpose?: 'any' | 'maskable' | 'monochrome';
}>;
/**
* The start_url member is a string that represents the start URL of the web application — the preferential URL that
* should be loaded when the user launches the web application (e.g., when the user taps on the web application's icon
* from a device's application menu or homescreen).
*/
start_url?: string;
/**
* The display member is a string that determines the developers’ preferred display mode for the website.
* The display mode changes how much of browser UI is shown to the user and can range from a browser (when the full
* browser window is shown) to standalone (when the app is run without any browser UI).
*
* The default for display is `browser`, which results in the normal browser UI being shown.
*
* The full options are:
* - `fullscreen`: All of the available display area is used and no user agent chrome is shown.
* - `standalone`: The application will look and feel like a standalone application. This can include the application having a different window, its own icon in the application launcher, etc.
* - `minimal-ui`: The application will look and feel like a standalone application, but will have a minimal set of UI elements for controlling navigation.
* - `browser`: The application opens in a conventional browser tab or new window, depending on the browser and platform.
*/
display?: 'fullscreen' | 'standalone' | 'minimal-ui' | 'browser';
display_override?: Array<'window-controls-overlay' | 'bordered' | 'standard'>;
/**
* The orientation member is a string that represents the default orientation of the web application.
* The value must be a string set to one of the following values:
* - `any`
* - `natural`
* - `landscape`
* - `landscape-primary`
* - `landscape-secondary`
* - `portrait`
* - `portrait-primary`
* - `portrait-secondary`
*/
orientation?:
| 'any'
| 'natural'
| 'landscape'
| 'landscape-primary'
| 'landscape-secondary'
| 'portrait'
| 'portrait-primary'
| 'portrait-secondary';
/**
* The dir member is a string that represents the directionality of the web application.
*
* The value must be a string set to one of the following values:
* - `ltr`: Left to right
* - `rtl`: Right to left
* - `auto`: Let the user agent decide based on the value of the `lang` attribute on the root element
*/
dir?: 'ltr' | 'rtl' | 'auto';
/**
* The lang member is a string that represents the primary language for the [localizable members](https://www.w3.org/TR/appmanifest/#dfn-localizable-members)
* of the manifest (as knowing the language can also help with directionality).
*/
lang?: string;
prefer_related_applications?: boolean;
related_applications?: Array<{
platform: string;
url?: string;
id?: string;
min_version?: string;
fingerprints?: Array<{
type: string;
value: string;
}>;
}>;
/**
* The scope member is a string that represents the navigation scope of this web application's application context.
*/
scope?: string;
screenshots?: Array<{
src: string;
sizes?: string;
type?: string;
platform?: string;
label?: string;
form_factor?: 'narrow' | 'wide';
}>;
shortcuts?: Array<{
name?: string;
short_name?: string;
description?: string;
url?: string;
icons?: Array<{
src: string;
sizes?: string;
type?: string;
purpose?: 'any' | 'maskable' | 'monochrome';
}>;
}>;
share_target?: {
action?: string;
method?: 'GET' | 'POST';
enctype?: string;
params?: {
[key: string]: {
name?: string;
title?: string;
description?: string;
};
};
};
protocol_handlers?: Array<{
protocol: string;
url: string;
}>;
note?: string;
/**
* The background_color member defines a placeholder background color for the application page to display before its stylesheet is loaded.
*/
background_color?: string;
/**
* The theme_color member is a string that defines the default theme color for the application.
*/
theme_color?: string;
categories?: Array<string>;
iarc_rating_ids?: Array<string>;
}
4 changes: 2 additions & 2 deletions packages/dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"@babel/traverse": "^7.24.8",
"@rollup/plugin-commonjs": "^26.0.1",
"@rollup/plugin-node-resolve": "^15.2.3",
"chokidar": "^3.6.0",
"chokidar": "^4.0.1",
"fast-glob": "^3.3.2",
"fs-extra": "^11.2.0",
"lodash": "^4.17.21",
Expand All @@ -54,7 +54,7 @@
"devDependencies": {
"@remix-pwa/eslint-config": "^0.0.0",
"@remix-pwa/lint-staged-config": "^0.0.0",
"@remix-run/dev": "^2.10.3",
"@remix-run/dev": "^2.12.1",
"@types/babel__core": "^7.20.5",
"@types/babel__generator": "^7.6.8",
"@types/babel__traverse": "^7.20.6",
Expand Down
4 changes: 4 additions & 0 deletions packages/dev/src/__test__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ describe('Plugin resolver test suite', () => {
'process.env.NODE_ENV': 'production',
'process.env.__REMIX_PWA_SPA_MODE': 'false',
},
injectSWRegister: true,
workerSourceMap: false,
publicPath: '/build/',
entryWorkerFile: 'entry.worker.ts',
Expand Down Expand Up @@ -112,6 +113,7 @@ describe('Plugin resolver test suite', () => {
appDirectory: '/Users/ryan/Projects/remix-pwa/app',
ignoredSWRouteFiles: [],
entryWorkerFile: 'entry.worker.ts',
injectSWRegister: true,
publicPath: '/build/',
workerEntryPoint: '@remix-pwa/worker-runtime',
workerSourceMap: false,
Expand Down Expand Up @@ -144,6 +146,7 @@ describe('Plugin resolver test suite', () => {
'process.env.__REMIX_PWA_SPA_MODE': 'false',
},
entryWorkerFile: 'entry.worker.ts',
injectSWRegister: true,
workerEntryPoint: '@remix-pwa/worker-runtime',
scope: '/',
rootDirectory: '/Users/ryan/Projects/remix-pwa',
Expand Down Expand Up @@ -182,6 +185,7 @@ describe('Plugin resolver test suite', () => {
'process.env.API_URL': 'https://api.example.com',
},
entryWorkerFile: 'entry.worker.ts',
injectSWRegister: true,
workerEntryPoint: '@remix-pwa/worker-runtime',
scope: '/',
rootDirectory: '/Users/ryan/Projects/remix-pwa',
Expand Down
69 changes: 69 additions & 0 deletions packages/dev/src/plugins/__test__/virtual-sw-plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,33 @@ describe('Remix PWA Vite VirtualSW Plugin', () => {
caseSensitive: true,`
);
});

test('should skip worker route APIs if single fetch is enabled', async () => {
const { createRouteManifest } = await import('../virtual-sw');
const routes = {
'routes/home': {
id: 'routes/home',
parentId: 'root',
path: '/home',
index: false,
caseSensitive: true,
file: 'home.tsx',
},
'routes/about': {
id: 'routes/about',
parentId: 'root',
path: '/about',
index: false,
caseSensitive: true,
file: 'about.tsx',
},
} as RouteManifest;

const result = await createRouteManifest(routes, '/', [], true);

expect(result).not.contain('workerLoader');
expect(result).not.contain('workerAction');
});
});
});

Expand Down Expand Up @@ -270,10 +297,18 @@ describe('Remix PWA Vite VirtualSW Plugin', () => {
},
} as RouteManifest,
},
viteConfig: {
logger: {
warnOnce: (str: string) => str,
},
},
isRemixDevServer: true,
__remixPluginContext: {
remixConfig: {
buildDirectory: '/build/',
future: {
unstable_singleFetch: false,
},
},
},
} as unknown as PWAPluginContext;
Expand Down Expand Up @@ -344,6 +379,21 @@ const a = 1;`);
expect(await plugin[1].load('\0virtual:entry-sw')).toContain('export const routes = {');
expect(await plugin[1].load('\0virtual:entry-sw')).toContain('export const entry = { module: entryWorker }');
});

test('should not include worker route module imports when single fetch is enabled', async () => {
mockContext.__remixPluginContext.remixConfig = {
...mockContext.__remixPluginContext.remixConfig,
// @ts-ignore We don't care about the rest
future: {
unstable_singleFetch: true,
},
};

const _plugin = (await import('../virtual-sw')).VirtualSWPlugins;
plugin = _plugin(mockContext as PWAPluginContext);

expect(await plugin[1].load('\0virtual:entry-sw')).not.contain('import * as route');
});
});

describe('Virtual Routes Plugin', () => {
Expand Down Expand Up @@ -371,6 +421,25 @@ const a = 1;`);

expect(result).toBe('module.exports = {}');
});

test('should return an empty module always if the single fetch is enabled', async () => {
mockContext.__remixPluginContext.remixConfig = {
...mockContext.__remixPluginContext.remixConfig,
// @ts-ignore We don't care about the rest
future: {
unstable_singleFetch: true,
},
};

const _plugin = (await import('../virtual-sw')).VirtualSWPlugins;
plugin = _plugin(mockContext as PWAPluginContext);

const homeResult = await plugin[2].load('virtual:worker:routes/home.tsx');
const aboutResult = await plugin[2].load('virtual:worker:routes/about.tsx');

expect(homeResult).toBe(undefined);
expect(aboutResult).toBe(undefined);
});
});

describe('Virtual Assets Plugin', () => {
Expand Down
15 changes: 13 additions & 2 deletions packages/dev/src/plugins/bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,22 @@ const transformedObject = (obj: Record<string, string>) =>
Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, JSON.stringify(value)]));

export async function buildWorker(_ctx: PWAPluginContext) {
const DEFAULT_VARS = {
const DEFAULT_VARS: Record<string, string | any> = {
'process.env.NODE_ENV': _ctx.isDev ? 'development' : 'production',
'process.env.__REMIX_PWA_SPA_MODE': _ctx.__remixPluginContext.remixConfig.ssr ? 'false' : 'true',
'process.env.__REMIX_SINGLE_FETCH': _ctx.__remixPluginContext.remixConfig.future.unstable_singleFetch
? 'true'
: 'false',
};

DEFAULT_VARS['process.env'] = JSON.stringify(
Object.fromEntries(
Object.entries(DEFAULT_VARS)
.filter(([key]) => key.startsWith('process.env.'))
.map(([key, value]) => [key.replace('process.env.', ''), value])
)
);

try {
await build({
logLevel: 'error',
Expand Down Expand Up @@ -122,7 +133,7 @@ export function BundlerPlugin(ctx: PWAPluginContext): Plugin {
return testString.startsWith('.');
},
followSymlinks: false,
disableGlobbing: false,
// disableGlobbing: false, // if an error arises 👈
});

const shouldAppReload = (path: string) => {
Expand Down
13 changes: 12 additions & 1 deletion packages/dev/src/plugins/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,18 @@ export function LoaderPlugin(ctx: PWAPluginContext): Plugin {
name: 'vite-plugin-remix-pwa:loader',
enforce: 'pre',
transform(code, id) {
if (Array.isArray(id.match(/root\.(tsx|jsx)$/)) && ctx.options.registerSW === 'script') {
if (
Array.isArray(id.match(/root\.(tsx|jsx)$/)) &&
(ctx.options.registerSW === 'script' || ctx.options.injectSWRegister)
) {
if (code.includes('<PWAScripts')) {
ctx.viteConfig.logger.warnOnce(
'💥 Usage of `PWAScripts` disables Service Worker injection! Either remove it or disable `injectSWRegister`'
);

return code;
}

return code.replace(
'</head>',
[
Expand Down
Loading
Loading