Skip to content

Commit

Permalink
feat: add .js file extension to built output + unwrap folder import/e…
Browse files Browse the repository at this point in the history
…xport to support native ESM within browser (#30770)
  • Loading branch information
Hotell authored Mar 14, 2024
1 parent 9865fa9 commit e4022e2
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 94 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "feat: enable .js extension addition and directory import/export unwrapping in build output",
"packageName": "@fluentui/tokens",
"email": "[email protected]",
"dependentChangeType": "patch"
}
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@
"@storybook/manager-webpack5": "6.5.15",
"@storybook/react": "6.5.15",
"@storybook/theming": "6.5.15",
"@swc/cli": "0.1.62",
"@swc/core": "1.3.87",
"@swc/cli": "0.1.65",
"@swc/core": "1.4.7",
"@swc/helpers": "0.5.1",
"@testing-library/dom": "8.11.3",
"@testing-library/jest-dom": "5.16.5",
Expand Down Expand Up @@ -320,7 +320,7 @@
"strip-ansi": "6.0.0",
"style-loader": "2.0.0",
"swc-loader": "0.2.3",
"swc-plugin-de-indent-template-literal": "1.0.0",
"swc-plugin-de-indent-template-literal": "1.4.0",
"syncpack": "10.6.1",
"tachometer": "0.7.0",
"terser": "5.28.1",
Expand Down
1 change: 1 addition & 0 deletions packages/tokens/.swcrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"/**/*.test.tsx"
],
"jsc": {
"baseUrl": ".",
"parser": {
"syntax": "typescript",
"tsx": true,
Expand Down
1 change: 1 addition & 0 deletions scripts/tasks/src/swc/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { swc } from './swc';
32 changes: 17 additions & 15 deletions scripts/tasks/src/swc.ts → scripts/tasks/src/swc/swc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,35 @@ import fs from 'fs';
import path from 'path';

import { transform } from '@swc/core';
import type { Options as SwcOptions } from '@swc/core';
import glob from 'glob';
import * as match from 'micromatch';
import * as micromatch from 'micromatch';

type Options = SwcOptions & { module: { type: 'es6' | 'commonjs' | 'amd' }; outputPath: string };
import { Options } from './types';
import { postprocessOutput } from './utils';

async function swcTransform(options: Options) {
const { outputPath, module } = options;
const packageRoot = process.cwd();
const sourceRootDirName = module.type === 'es6' ? 'src' : 'lib';
const { outputPath, module, root: packageRoot = process.cwd() } = options;

const moduleType = module.type;
const sourceRootDirName = moduleType === 'es6' ? 'src' : 'lib';

let sourceFiles: string[] = [];

if (module.type === 'es6') {
if (moduleType === 'es6') {
sourceFiles = glob.sync(`${sourceRootDirName}/**/*.{ts,tsx}`);
}

if (module.type === 'commonjs' || module.type === 'amd') {
if (moduleType === 'commonjs' || moduleType === 'amd') {
sourceFiles = glob.sync(`${sourceRootDirName}/**/*.js`);
}

const swcConfig = JSON.parse(fs.readFileSync(path.resolve(packageRoot, '.swcrc'), 'utf-8'));
const enableResolveFully = Boolean(swcConfig.jsc.baseUrl);
const tsFileExtensionRegex = /\.(tsx|ts)$/;

for (const fileName of sourceFiles) {
const srcFilePath = path.resolve(packageRoot, fileName);
const isFileExcluded = match.isMatch(srcFilePath, swcConfig.exclude, { contains: true });
const isFileExcluded = micromatch.isMatch(srcFilePath, swcConfig.exclude, { contains: true });

if (isFileExcluded) {
continue;
Expand All @@ -38,19 +40,19 @@ async function swcTransform(options: Options) {

const result = await transform(sourceCode, {
filename: fileName,
module: { type: module.type },
module: { type: moduleType, resolveFully: enableResolveFully },
sourceFileName: path.basename(fileName),
outputPath,
});

// Strip @jsx comments, see https://github.com/microsoft/fluentui/issues/29126
const resultCode = result.code
.replace('/** @jsxRuntime automatic */', '')
.replace('/** @jsxImportSource @fluentui/react-jsx-runtime */', '');
const resultCode = postprocessOutput(result.code, {
addExplicitJsExtensionToImports: enableResolveFully,
moduleType,
});

const compiledFilePath = path.resolve(packageRoot, fileName.replace(`${sourceRootDirName}`, outputPath));

//Create directory folder for new compiled file(s) to live in.
// Create directory folder for new compiled file(s) to live in.
await fs.promises.mkdir(compiledFilePath.replace(path.basename(compiledFilePath), ''), { recursive: true });

const compiledFilePathJS = `${compiledFilePath.replace(tsFileExtensionRegex, '.js')}`;
Expand Down
9 changes: 9 additions & 0 deletions scripts/tasks/src/swc/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { type Options as SwcOptions } from '@swc/core';

declare module '@swc/core' {
interface BaseModuleConfig {
resolveFully?: boolean;
}
}

export type Options = SwcOptions & Required<Pick<SwcOptions, 'module' | 'outputPath'>> & { root?: string };
59 changes: 59 additions & 0 deletions scripts/tasks/src/swc/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { stripIndents } from '@nx/devkit';

import { addJsExtensionToImports } from './utils';

describe(`utils`, () => {
describe(`#addJsExtensionToImports`, () => {
it(`should not transform anything if non supported module type is specified`, () => {
const code = stripIndents`
export { themeToTokensObject } from './themeToTokensObject';
`;

let actual = addJsExtensionToImports(code, 'umd');

expect(actual).toEqual(code);

actual = addJsExtensionToImports(code, 'amd');

expect(actual).toEqual(code);

actual = addJsExtensionToImports(code, 'systemjs');

expect(actual).toEqual(code);
});

it(`should add .js extensions for esm`, () => {
const code = stripIndents`
export { themeToTokensObject } from './themeToTokensObject';
export { tokens } from './tokens';
export { typographyStyles } from './global/index.js';
`;

const actual = addJsExtensionToImports(code, 'es6');
const expected = stripIndents`
export { themeToTokensObject } from './themeToTokensObject.js';
export { tokens } from './tokens.js';
export { typographyStyles } from './global/index.js';
`;

expect(actual).toEqual(expected);
});

it(`should add .js extensions for commonjs`, () => {
const code = stripIndents`
const _themeToTokensObject = require("./themeToTokensObject");
const _tokens = require("./tokens");
const _index2 = require("./global/index.js");
`;

const actual = addJsExtensionToImports(code, 'commonjs');
const expected = stripIndents`
const _themeToTokensObject = require("./themeToTokensObject.js");
const _tokens = require("./tokens.js");
const _index2 = require("./global/index.js");
`;

expect(actual).toEqual(expected);
});
});
});
37 changes: 37 additions & 0 deletions scripts/tasks/src/swc/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { type Options } from './types';

const importPaths = {
es6: /from\s+["']([^"']+)["']/g,
commonjs: /require\(["']([^"']+)["']\)/g,
};
export function addJsExtensionToImports(code: string, type: Options['module']['type'] = 'es6') {
if (!(type === 'es6' || type === 'commonjs')) {
return code;
}

const regex = importPaths[type];

return code.replace(regex, (match, importPath) => {
if (importPath.endsWith('.js')) {
return match;
}
return match.replace(importPath, importPath + '.js');
});
}

export function postprocessOutput(
code: string,
options: { moduleType: Options['module']['type']; addExplicitJsExtensionToImports: boolean },
) {
// Strip @jsx comments, see https://github.com/microsoft/fluentui/issues/29126
let resultCode = code
.replace('/** @jsxRuntime automatic */', '')
.replace('/** @jsxImportSource @fluentui/react-jsx-runtime */', '');

// TODO: Remove after swc implement proper js extension addition https://github.com/microsoft/fluentui/issues/30634
resultCode = options.addExplicitJsExtensionToImports
? addJsExtensionToImports(resultCode, options.moduleType)
: resultCode;

return resultCode;
}
Loading

0 comments on commit e4022e2

Please sign in to comment.