font-stylesheet-gathering-plugin.js.map 15.3 KB
Newer Older
1
{"version":3,"sources":["../../../../build/webpack/plugins/font-stylesheet-gathering-plugin.ts"],"names":["minifyCss","css","excludeAll","discardComments","normalizeWhitespace","exclude","process","from","undefined","then","res","FontStylesheetGatheringPlugin","constructor","isLikeServerless","compiler","gatheredStylesheets","manifestContent","parserHandler","factory","JS_TYPES","type","hooks","parser","for","tap","name","evaluate","node","state","module","resource","includes","result","BasicEvaluatedExpression","setRange","range","setExpression","setIdentifier","isWebpack5","getMembers","jsxNodeHandler","arguments","length","isNodeCreatingLinkElement","propsNode","props","properties","forEach","prop","key","value","rel","href","OPTIMIZED_FONT_PROVIDERS","some","url","startsWith","push","buildInfo","valueDependencies","set","FONT_MANIFEST","call","apply","normalModuleFactory","make","tapAsync","compilation","cb","mainTemplate","requireExtensions","source","requireFn","JSON","stringify","finishModules","modules","modulesFinished","fontStylesheets","fontUrls","Set","fontDependencies","get","v","add","Array","fontDefinitionPromises","map","promiseIndex","content","assets","sources","RawSource","processAssets","stage","webpack","Compilation","PROCESS_ASSETS_STAGE_ADDITIONS","callee","componentNode"],"mappings":"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","sourcesContent":["import {\n  webpack,\n  BasicEvaluatedExpression,\n  isWebpack5,\n  sources,\n} from 'next/dist/compiled/webpack/webpack'\nimport { namedTypes } from 'ast-types'\nimport {\n  getFontDefinitionFromNetwork,\n  FontManifest,\n} from '../../../next-server/server/font-utils'\nimport postcss from 'postcss'\nimport minifier from 'cssnano-simple'\nimport {\n  FONT_MANIFEST,\n  OPTIMIZED_FONT_PROVIDERS,\n} from '../../../next-server/lib/constants'\n\nfunction minifyCss(css: string): Promise<string> {\n  return postcss([\n    minifier({\n      excludeAll: true,\n      discardComments: true,\n      normalizeWhitespace: { exclude: false },\n    }),\n  ])\n    .process(css, { from: undefined })\n    .then((res) => res.css)\n}\n\nexport class FontStylesheetGatheringPlugin {\n  compiler?: webpack.Compiler\n  gatheredStylesheets: Array<string> = []\n  manifestContent: FontManifest = []\n  isLikeServerless: boolean\n\n  constructor({ isLikeServerless }: { isLikeServerless: boolean }) {\n    this.isLikeServerless = isLikeServerless\n  }\n\n  private parserHandler = (\n    factory: webpack.compilation.NormalModuleFactory\n  ): void => {\n    const JS_TYPES = ['auto', 'esm', 'dynamic']\n    // Do an extra walk per module and add interested visitors to the walk.\n    for (const type of JS_TYPES) {\n      factory.hooks.parser\n        .for('javascript/' + type)\n        .tap(this.constructor.name, (parser: any) => {\n          /**\n           * Webpack fun facts:\n           * `parser.hooks.call.for` cannot catch calls for user defined identifiers like `__jsx`\n           * it can only detect calls for native objects like `window`, `this`, `eval` etc.\n           * In order to be able to catch calls of variables like `__jsx`, first we need to catch them as\n           * Identifier and then return `BasicEvaluatedExpression` whose `id` and `type` webpack matches to\n           * invoke hook for call.\n           * See: https://github.com/webpack/webpack/blob/webpack-4/lib/Parser.js#L1931-L1932.\n           */\n          parser.hooks.evaluate\n            .for('Identifier')\n            .tap(this.constructor.name, (node: namedTypes.Identifier) => {\n              // We will only optimize fonts from first party code.\n              if (parser?.state?.module?.resource.includes('node_modules')) {\n                return\n              }\n              let result\n              if (node.name === '_jsx' || node.name === '__jsx') {\n                result = new BasicEvaluatedExpression()\n                // @ts-ignore\n                result.setRange(node.range)\n                result.setExpression(node)\n                result.setIdentifier(node.name)\n\n                // This was added webpack 5.\n                if (isWebpack5) {\n                  result.getMembers = () => []\n                }\n              }\n              return result\n            })\n\n          const jsxNodeHandler = (node: namedTypes.CallExpression) => {\n            if (node.arguments.length !== 2) {\n              // A font link tag has only two arguments rel=stylesheet and href='...'\n              return\n            }\n            if (!isNodeCreatingLinkElement(node)) {\n              return\n            }\n\n            // node.arguments[0] is the name of the tag and [1] are the props.\n            const propsNode = node.arguments[1] as namedTypes.ObjectExpression\n            const props: { [key: string]: string } = {}\n            propsNode.properties.forEach((prop) => {\n              if (prop.type !== 'Property') {\n                return\n              }\n              if (\n                prop.key.type === 'Identifier' &&\n                prop.value.type === 'Literal'\n              ) {\n                props[prop.key.name] = prop.value.value as string\n              }\n            })\n            if (\n              !props.rel ||\n              props.rel !== 'stylesheet' ||\n              !props.href ||\n              !OPTIMIZED_FONT_PROVIDERS.some((url) =>\n                props.href.startsWith(url)\n              )\n            ) {\n              return false\n            }\n\n            this.gatheredStylesheets.push(props.href)\n\n            if (isWebpack5) {\n              const buildInfo = parser?.state?.module?.buildInfo\n\n              if (buildInfo) {\n                buildInfo.valueDependencies.set(\n                  FONT_MANIFEST,\n                  this.gatheredStylesheets\n                )\n              }\n            }\n          }\n\n          // React JSX transform:\n          parser.hooks.call\n            .for('_jsx')\n            .tap(this.constructor.name, jsxNodeHandler)\n          // Next.js JSX transform:\n          parser.hooks.call\n            .for('__jsx')\n            .tap(this.constructor.name, jsxNodeHandler)\n          // New React JSX transform:\n          parser.hooks.call\n            .for('imported var')\n            .tap(this.constructor.name, jsxNodeHandler)\n        })\n    }\n  }\n\n  public apply(compiler: webpack.Compiler) {\n    this.compiler = compiler\n    compiler.hooks.normalModuleFactory.tap(\n      this.constructor.name,\n      this.parserHandler\n    )\n    compiler.hooks.make.tapAsync(this.constructor.name, (compilation, cb) => {\n      if (this.isLikeServerless) {\n        /**\n         * Inline font manifest for serverless case only.\n         * For target: server drive the manifest through physical file and less of webpack magic.\n         */\n        const mainTemplate = compilation.mainTemplate\n        mainTemplate.hooks.requireExtensions.tap(\n          this.constructor.name,\n          (source: string) => {\n            return `${source}\n                // Font manifest declaration\n                ${\n                  isWebpack5 ? '__webpack_require__' : mainTemplate.requireFn\n                }.__NEXT_FONT_MANIFEST__ = ${JSON.stringify(\n              this.manifestContent\n            )};\n            // Enable feature:\n            process.env.__NEXT_OPTIMIZE_FONTS = JSON.stringify(true);`\n          }\n        )\n      }\n      compilation.hooks.finishModules.tapAsync(\n        this.constructor.name,\n        async (modules: any, modulesFinished: Function) => {\n          let fontStylesheets = this.gatheredStylesheets\n\n          if (isWebpack5) {\n            const fontUrls = new Set<string>()\n            modules.forEach((module: any) => {\n              const fontDependencies = module?.buildInfo?.valueDependencies?.get(\n                FONT_MANIFEST\n              )\n              if (fontDependencies) {\n                fontDependencies.forEach((v: string) => fontUrls.add(v))\n              }\n            })\n\n            fontStylesheets = Array.from(fontUrls)\n          }\n\n          const fontDefinitionPromises = fontStylesheets.map((url) =>\n            getFontDefinitionFromNetwork(url)\n          )\n\n          this.manifestContent = []\n          for (let promiseIndex in fontDefinitionPromises) {\n            const css = await fontDefinitionPromises[promiseIndex]\n\n            if (css) {\n              const content = await minifyCss(css)\n              this.manifestContent.push({\n                url: fontStylesheets[promiseIndex],\n                content,\n              })\n            }\n          }\n          if (!isWebpack5) {\n            compilation.assets[FONT_MANIFEST] = new sources.RawSource(\n              JSON.stringify(this.manifestContent, null, '  ')\n            )\n          }\n          modulesFinished()\n        }\n      )\n      cb()\n    })\n\n    if (isWebpack5) {\n      compiler.hooks.make.tap(this.constructor.name, (compilation) => {\n        // @ts-ignore TODO: Remove ignore when webpack 5 is stable\n        compilation.hooks.processAssets.tap(\n          {\n            name: this.constructor.name,\n            // @ts-ignore TODO: Remove ignore when webpack 5 is stable\n            stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,\n          },\n          (assets: any) => {\n            assets['../' + FONT_MANIFEST] = new sources.RawSource(\n              JSON.stringify(this.manifestContent, null, '  ')\n            )\n          }\n        )\n      })\n    }\n  }\n}\n\nfunction isNodeCreatingLinkElement(node: namedTypes.CallExpression) {\n  const callee = node.callee as namedTypes.Identifier\n  if (callee.type !== 'Identifier') {\n    return false\n  }\n  const componentNode = node.arguments[0] as namedTypes.Literal\n  if (componentNode.type !== 'Literal') {\n    return false\n  }\n  // React has pragma: _jsx.\n  // Next has pragma: __jsx.\n  return (\n    (callee.name === '_jsx' || callee.name === '__jsx') &&\n    componentNode.value === 'link'\n  )\n}\n"]}