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

Support CSS from CDN in esm-views #1757

Merged
merged 20 commits into from
May 30, 2022
Merged
Show file tree
Hide file tree
Changes from 19 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
5 changes: 5 additions & 0 deletions .changeset/purple-poets-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"modular-scripts": patch
---

Webpack build: Support CSS from CDN in esm-views
42 changes: 42 additions & 0 deletions packages/modular-scripts/react-scripts/config/cdnStyleLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict';
// This is a pitching loader, which means that it will exclude other loaders if its pitch method returns.
// pitch methods are evaluated left to right and the correspondent default methods are evaluated later, right to left
// For this reason, this loader must be first in the chain of other style loaders.
// It will just set the output and interrupt if pitch returns and it will just act as a bypass of the chain output otherwise
// If it existed another method of redirecting an external to a loader, it'd be simpler to just get rid of these pitch restrictions

module.exports = function cdnStyleLoader(asset) {
// noop. Just return the translated asset if the pitching doesn't return (and the chain is processed)
return asset;
};

module.exports.pitch = function () {
benpryke marked this conversation as resolved.
Show resolved Hide resolved
const { info, dependencyMap } = this.getOptions();
const { descriptionData } = info;
const dependency = dependencyMap[descriptionData.name];

if (dependency) {
// The submodule bit is the relative path in the resolver data. Use URL to normalize paths.
const submodule = this._module.resourceResolveData.relativePath;
const dependencyPath = new URL(dependency).pathname;
const dependencyUrl = submodule
? new URL(`${dependencyPath}/${submodule}`, dependency).href
: dependency;
return generateStyleInjector(dependencyUrl);
}
};

function generateStyleInjector(url) {
return `
const link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about .less files?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we're expecting to process any files coming from the CDN at runtime - the expectation is that the CDN provides files that are already processed by the build process of the dependency.

link.href = '${url}';
if (!document.head) {
const newHead = document.createElement('head');
document.documentElement.insertBefore(newHead, document.body || null);
}
const head = document.head;
head.appendChild(link);
`;
}
38 changes: 28 additions & 10 deletions packages/modular-scripts/react-scripts/config/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
const { info } = require('../../react-dev-utils/logger');
const logger = require('../../react-dev-utils/logger');
const InterpolateHtmlPlugin = require('../../react-dev-utils/InterpolateHtmlPlugin');
const WatchMissingNodeModulesPlugin = require('../../react-dev-utils/WatchMissingNodeModulesPlugin');
const ModuleScopePlugin = require('../../react-dev-utils/ModuleScopePlugin');
Expand Down Expand Up @@ -102,8 +102,18 @@ module.exports = function (webpackEnv) {
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));

// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const getStyleLoaders = (cssOptions, preProcessor, includeEsmLoader) => {
const loaders = [
// This loader translates external css dependencies if we're using a CDN
// Since it's a pitching loader, it's important that it stays at the top
// excluding all the others in the chain if it's triggered
includeEsmLoader &&
function externalStyleLoader(info) {
benpryke marked this conversation as resolved.
Show resolved Hide resolved
return {
loader: require.resolve('./cdnStyleLoader'),
options: { info, dependencyMap },
};
},
isEnvDevelopment && require.resolve('style-loader'),
isEnvProduction && {
loader: MiniCssExtractPlugin.loader,
Expand Down Expand Up @@ -160,6 +170,7 @@ module.exports = function (webpackEnv) {
},
);
}

return loaders;
};

Expand All @@ -173,7 +184,9 @@ module.exports = function (webpackEnv) {
if (
parsedModule &&
parsedModule.dependencyName &&
dependencyMap[parsedModule.dependencyName]
dependencyMap[parsedModule.dependencyName] &&
// If this is an absolute export of css we need to deal with it in the loader
!request.endsWith('.css')
) {
const { dependencyName, submodule } = parsedModule;

Expand Down Expand Up @@ -464,12 +477,17 @@ module.exports = function (webpackEnv) {
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
}),
use: getStyleLoaders(
{
importLoaders: 1,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
},
undefined,
isEsmView,
),

// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
Expand Down Expand Up @@ -745,7 +763,7 @@ module.exports = function (webpackEnv) {
try {
require.resolve(plugin.package);
} catch (err) {
info(
logger.info(
`It appears you're using ${chalk.cyan(
dependency,
)}. Run ${chalk.cyan.bold(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import get from 'lodash/get';
import merge from 'lodash.merge';
import { difference } from 'lodash';
import 'regular-table/dist/css/material.css';

export default function SampleView(): JSX.Element {
return (
Expand Down
Loading