123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- "use strict";
- Object.defineProperty(exports, "__esModule", {
- value: true
- });
- var _webpack = require("next/dist/compiled/webpack/webpack");
- var _fontUtils = require("../../../server/font-utils");
- var _postcss = _interopRequireDefault(require("postcss"));
- var _cssnanoSimple = _interopRequireDefault(require("next/dist/compiled/cssnano-simple"));
- var _constants = require("../../../shared/lib/constants");
- var Log = _interopRequireWildcard(require("../../output/log"));
- function _interopRequireDefault(obj) {
- return obj && obj.__esModule ? obj : {
- default: obj
- };
- }
- function _getRequireWildcardCache() {
- if (typeof WeakMap !== "function") return null;
- var cache = new WeakMap();
- _getRequireWildcardCache = function() {
- return cache;
- };
- return cache;
- }
- function _interopRequireWildcard(obj) {
- if (obj && obj.__esModule) {
- return obj;
- }
- if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
- return {
- default: obj
- };
- }
- var cache = _getRequireWildcardCache();
- if (cache && cache.has(obj)) {
- return cache.get(obj);
- }
- var newObj = {};
- var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
- for(var key in obj){
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
- var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
- if (desc && (desc.get || desc.set)) {
- Object.defineProperty(newObj, key, desc);
- } else {
- newObj[key] = obj[key];
- }
- }
- }
- newObj.default = obj;
- if (cache) {
- cache.set(obj, newObj);
- }
- return newObj;
- }
- function minifyCss(css) {
- return (0, _postcss).default([
- (0, _cssnanoSimple).default({
- excludeAll: true,
- discardComments: true,
- normalizeWhitespace: {
- exclude: false
- }
- }, _postcss.default),
- ]).process(css, {
- from: undefined
- }).then((res)=>res.css);
- }
- function isNodeCreatingLinkElement(node) {
- const callee = node.callee;
- if (callee.type !== "Identifier") {
- return false;
- }
- const componentNode = node.arguments[0];
- if (componentNode.type !== "Literal") {
- return false;
- }
- // React has pragma: _jsx.
- // Next has pragma: __jsx.
- return (callee.name === "_jsx" || callee.name === "__jsx") && componentNode.value === "link";
- }
- class FontStylesheetGatheringPlugin {
- gatheredStylesheets = [];
- manifestContent = [];
- constructor({ isLikeServerless , adjustFontFallbacks }){
- this.isLikeServerless = isLikeServerless;
- this.adjustFontFallbacks = adjustFontFallbacks;
- }
- parserHandler = (factory)=>{
- const JS_TYPES = [
- "auto",
- "esm",
- "dynamic"
- ];
- // Do an extra walk per module and add interested visitors to the walk.
- for (const type of JS_TYPES){
- factory.hooks.parser.for("javascript/" + type).tap(this.constructor.name, (parser)=>{
- /**
- * Webpack fun facts:
- * `parser.hooks.call.for` cannot catch calls for user defined identifiers like `__jsx`
- * it can only detect calls for native objects like `window`, `this`, `eval` etc.
- * In order to be able to catch calls of variables like `__jsx`, first we need to catch them as
- * Identifier and then return `BasicEvaluatedExpression` whose `id` and `type` webpack matches to
- * invoke hook for call.
- * See: https://github.com/webpack/webpack/blob/webpack-4/lib/Parser.js#L1931-L1932.
- */ parser.hooks.evaluate.for("Identifier").tap(this.constructor.name, (node)=>{
- var ref, ref1;
- // We will only optimize fonts from first party code.
- if (parser == null ? void 0 : (ref = parser.state) == null ? void 0 : (ref1 = ref.module) == null ? void 0 : ref1.resource.includes("node_modules")) {
- return;
- }
- let result;
- if (node.name === "_jsx" || node.name === "__jsx") {
- result = new _webpack.BasicEvaluatedExpression();
- // @ts-ignore
- result.setRange(node.range);
- result.setExpression(node);
- result.setIdentifier(node.name);
- // This was added in webpack 5.
- result.getMembers = ()=>[];
- }
- return result;
- });
- const jsxNodeHandler = (node)=>{
- var ref, ref2;
- if (node.arguments.length !== 2) {
- // A font link tag has only two arguments rel=stylesheet and href='...'
- return;
- }
- if (!isNodeCreatingLinkElement(node)) {
- return;
- }
- // node.arguments[0] is the name of the tag and [1] are the props.
- const arg1 = node.arguments[1];
- const propsNode = arg1.type === "ObjectExpression" ? arg1 : undefined;
- const props = {};
- if (propsNode) {
- propsNode.properties.forEach((prop)=>{
- if (prop.type !== "Property") {
- return;
- }
- if (prop.key.type === "Identifier" && prop.value.type === "Literal") {
- props[prop.key.name] = prop.value.value;
- }
- });
- }
- if (!props.rel || props.rel !== "stylesheet" || !props.href || !_constants.OPTIMIZED_FONT_PROVIDERS.some(({ url })=>props.href.startsWith(url))) {
- return false;
- }
- this.gatheredStylesheets.push(props.href);
- const buildInfo = parser == null ? void 0 : (ref = parser.state) == null ? void 0 : (ref2 = ref.module) == null ? void 0 : ref2.buildInfo;
- if (buildInfo) {
- buildInfo.valueDependencies.set(_constants.FONT_MANIFEST, this.gatheredStylesheets);
- }
- };
- // React JSX transform:
- parser.hooks.call.for("_jsx").tap(this.constructor.name, jsxNodeHandler);
- // Next.js JSX transform:
- parser.hooks.call.for("__jsx").tap(this.constructor.name, jsxNodeHandler);
- // New React JSX transform:
- parser.hooks.call.for("imported var").tap(this.constructor.name, jsxNodeHandler);
- });
- }
- };
- apply(compiler) {
- this.compiler = compiler;
- compiler.hooks.normalModuleFactory.tap(this.constructor.name, this.parserHandler);
- compiler.hooks.make.tapAsync(this.constructor.name, (compilation, cb)=>{
- if (this.isLikeServerless) {
- /**
- * Inline font manifest for serverless case only.
- * For target: server drive the manifest through physical file and less of webpack magic.
- */ const mainTemplate = compilation.mainTemplate;
- mainTemplate.hooks.requireExtensions.tap(this.constructor.name, (source)=>{
- return `${source}
- // Font manifest declaration
- __webpack_require__.__NEXT_FONT_MANIFEST__ = ${JSON.stringify(this.manifestContent)};
- // Enable feature:
- process.env.__NEXT_OPTIMIZE_FONTS = JSON.stringify(true);`;
- });
- }
- compilation.hooks.finishModules.tapAsync(this.constructor.name, async (modules, modulesFinished)=>{
- let fontStylesheets = this.gatheredStylesheets;
- const fontUrls = new Set();
- modules.forEach((module)=>{
- var ref, ref3;
- const fontDependencies = module == null ? void 0 : (ref = module.buildInfo) == null ? void 0 : (ref3 = ref.valueDependencies) == null ? void 0 : ref3.get(_constants.FONT_MANIFEST);
- if (fontDependencies) {
- fontDependencies.forEach((v)=>fontUrls.add(v));
- }
- });
- fontStylesheets = Array.from(fontUrls);
- const fontDefinitionPromises = fontStylesheets.map((url)=>(0, _fontUtils).getFontDefinitionFromNetwork(url));
- this.manifestContent = [];
- for(let promiseIndex in fontDefinitionPromises){
- let css = await fontDefinitionPromises[promiseIndex];
- if (this.adjustFontFallbacks) {
- css += (0, _fontUtils).getFontOverrideCss(fontStylesheets[promiseIndex], css);
- }
- if (css) {
- try {
- const content = await minifyCss(css);
- this.manifestContent.push({
- url: fontStylesheets[promiseIndex],
- content
- });
- } catch (err) {
- Log.warn(`Failed to minify the stylesheet for ${fontStylesheets[promiseIndex]}. Skipped optimizing this font.`);
- console.error(err);
- }
- }
- }
- // @ts-expect-error invalid assets type
- compilation.assets[_constants.FONT_MANIFEST] = new _webpack.sources.RawSource(JSON.stringify(this.manifestContent, null, " "));
- modulesFinished();
- });
- cb();
- });
- compiler.hooks.make.tap(this.constructor.name, (compilation)=>{
- // @ts-ignore TODO: Remove ignore when webpack 5 is stable
- compilation.hooks.processAssets.tap({
- name: this.constructor.name,
- // @ts-ignore TODO: Remove ignore when webpack 5 is stable
- stage: _webpack.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
- }, (assets)=>{
- assets["../" + _constants.FONT_MANIFEST] = new _webpack.sources.RawSource(JSON.stringify(this.manifestContent, null, " "));
- });
- });
- }
- }
- exports.FontStylesheetGatheringPlugin = FontStylesheetGatheringPlugin;
- //# sourceMappingURL=font-stylesheet-gathering-plugin.js.map
|