Skip to content

Commit

Permalink
Merge pull request #269 from remix-pwa/v5-rc
Browse files Browse the repository at this point in the history
Remix PWA v5 Release Candidate
  • Loading branch information
ShafSpecs authored Oct 11, 2024
2 parents 3004f72 + 752b57a commit 04fd604
Show file tree
Hide file tree
Showing 60 changed files with 5,527 additions and 6,712 deletions.
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

0 comments on commit 04fd604

Please sign in to comment.