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

feat(plugin-tailwind): automatically read tailwind config file #4517

Merged
merged 10 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 7 additions & 0 deletions .changeset/modern-pants-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@modern-js/plugin-tailwindcss': patch
---

feat(plugin-tailwind): automatically read tailwind config file

feat(plugin-tailwind): 自动读取 tailwind config 文件
1 change: 1 addition & 0 deletions packages/cli/plugin-tailwind/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
},
"dependencies": {
"@modern-js/utils": "workspace:*",
"@modern-js/node-bundle-require": "workspace:*",
"babel-plugin-macros": "3.1.0",
"hoist-non-react-statics": "^3.3.2",
"@swc/helpers": "0.5.1"
Expand Down
74 changes: 22 additions & 52 deletions packages/cli/plugin-tailwind/src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,16 @@
import path from 'path';
import { fs, CONFIG_CACHE_DIR, globby, nanoid, slash } from '@modern-js/utils';
import { fs, CONFIG_CACHE_DIR, globby, slash } from '@modern-js/utils';
import type { LegacyAppTools, NormalizedConfig } from '@modern-js/app-tools';
import type { CliPlugin, ModuleTools } from '@modern-js/module-tools';
import designTokenPlugin from './design-token/cli';
import { getTailwindConfig } from './tailwind';
import { designTokenPlugin } from './design-token/cli';
import { getTailwindConfig, loadConfigFile } from './config';
import { getTailwindPath, getTailwindVersion } from './utils';
import {
template,
checkTwinMacroExist,
getTailwindPath,
getTailwindVersion,
getTwinMacroMajorVersion,
} from './utils';

const supportCssInJsLibrary = 'styled-components';

export const getRandomTwConfigFileName = (internalDirectory: string) => {
return slash(
path.join(
internalDirectory,
`tailwind.config.${Date.now()}.${nanoid()}.js`,
),
);
};

function getDefaultContent(appDirectory: string) {
const defaultContent = ['./src/**/*.{js,jsx,ts,tsx}'];

// Only add storybook and html config when they exist
// Otherwise, it will cause an unnecessary rebuild
if (fs.existsSync(path.join(appDirectory, 'storybook'))) {
defaultContent.push('./storybook/**/*');
}
if (fs.existsSync(path.join(appDirectory, 'config/html'))) {
defaultContent.push('./config/html/**/*.{html,ejs,hbs}');
}

return defaultContent;
}
getRandomTwConfigFileName,
} from './macro';

export const tailwindcssPlugin = (
{ pluginName } = {
Expand All @@ -51,14 +25,15 @@ export const tailwindcssPlugin = (
pluginName,
}),
],

setup: async api => {
const { appDirectory, internalDirectory } = api.useAppContext();
let internalTwConfigPath = '';
// When reinstalling dependencies, most of the time the project will be restarted
const haveTwinMacro = await checkTwinMacroExist(appDirectory);
const tailwindPath = getTailwindPath(appDirectory);
const tailwindVersion = getTailwindVersion(appDirectory);
const defaultContent = getDefaultContent(appDirectory);
const userTailwindConfig = await loadConfigFile(appDirectory);

return {
prepare() {
Expand Down Expand Up @@ -103,16 +78,13 @@ export const tailwindcssPlugin = (
const initTailwindConfig = () => {
if (!tailwindConfig) {
const modernConfig = api.useResolvedConfigContext();
tailwindConfig = getTailwindConfig(
tailwindConfig = getTailwindConfig({
appDirectory,
tailwindVersion,
modernConfig?.tools?.tailwindcss,
modernConfig?.source?.designSystem,
{
pureConfig: {
content: defaultContent,
},
},
);
userConfig: userTailwindConfig,
extraConfig: modernConfig?.tools?.tailwindcss,
designSystem: modernConfig?.source?.designSystem,
});
}
};

Expand All @@ -133,6 +105,7 @@ export const tailwindcssPlugin = (

babel: haveTwinMacro
? (_, { addPlugins }) => {
const supportCssInJsLibrary = 'styled-components';
initTailwindConfig();
addPlugins([
[
Expand All @@ -154,17 +127,13 @@ export const tailwindcssPlugin = (
beforeBuildTask(config) {
const modernConfig =
api.useResolvedConfigContext() as NormalizedConfig<ModuleTools>;
const { designSystem } = modernConfig;
const tailwindConfig = getTailwindConfig(
const tailwindConfig = getTailwindConfig({
appDirectory,
tailwindVersion,
config.style.tailwindcss,
designSystem,
{
pureConfig: {
content: defaultContent,
},
},
);
userConfig: userTailwindConfig,
extraConfig: config.style.tailwindcss,
designSystem: modernConfig.designSystem,
});

const tailwindPlugin = require(tailwindPath)(tailwindConfig);
if (Array.isArray(config.style.postcss.plugins)) {
Expand All @@ -175,6 +144,7 @@ export const tailwindcssPlugin = (

return config;
},

modifyLibuild(config, next) {
config.transformCache = false;
return next(config);
Expand Down
93 changes: 93 additions & 0 deletions packages/cli/plugin-tailwind/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import path from 'path';
import { fs, applyOptionsChain, findExists } from '@modern-js/utils';
import { cloneDeep } from '@modern-js/utils/lodash';
import { bundleRequire } from '@modern-js/node-bundle-require';
import type {
DesignSystem,
TailwindConfig,
ExtraTailwindConfig,
} from './types';

function getDefaultContent(appDirectory: string) {
const defaultContent = ['./src/**/*.{js,jsx,ts,tsx}'];

// Only add storybook and html config when they exist
// Otherwise, it will cause an unnecessary rebuild
if (fs.existsSync(path.join(appDirectory, 'storybook'))) {
defaultContent.push('./storybook/**/*');
}
if (fs.existsSync(path.join(appDirectory, 'config/html'))) {
defaultContent.push('./config/html/**/*.{html,ejs,hbs}');
}

return defaultContent;
}

const getPureDesignSystemConfig = (config: DesignSystem) => {
const pureConfig = cloneDeep(config);
delete pureConfig.supportStyledComponents;
return pureConfig;
};

const getV2PurgeConfig = (content: string[]) => ({
enabled: process.env.NODE_ENV === 'production',
layers: ['utilities'],
content,
});

export async function loadConfigFile(appDirectory: string) {
const extensions = ['ts', 'js', 'cjs', 'mjs'];
const configs = extensions.map(ext =>
path.resolve(appDirectory, `tailwind.config.${ext}`),
);
const configFile = findExists(configs);

if (configFile) {
const mod = await bundleRequire(configFile);
return mod.default || mod;
}

return {};
}

/**
* Config priority:
* `source.designSystem` > `tools.tailwindcss` (extraConfig) > `tailwind.config.*` (userConfig) > `defaultConfig`
*/
const getTailwindConfig = ({
tailwindVersion,
appDirectory,
userConfig,
extraConfig,
designSystem,
}: {
tailwindVersion: '2' | '3';
appDirectory: string;
userConfig: TailwindConfig;
extraConfig?: ExtraTailwindConfig;
designSystem?: DesignSystem;
}) => {
const content = getDefaultContent(appDirectory);

let tailwindConfig: TailwindConfig =
tailwindVersion === '3'
? { content }
: { purge: getV2PurgeConfig(content) };

Object.assign(tailwindConfig, userConfig);

tailwindConfig = extraConfig
? applyOptionsChain(tailwindConfig, extraConfig)
: tailwindConfig;

const designSystemConfig = getPureDesignSystemConfig(designSystem ?? {});

// if designSystem config is used, it will override the theme config of tailwind
if (designSystemConfig && Object.keys(designSystemConfig).length > 0) {
tailwindConfig.theme = designSystemConfig;
}

return tailwindConfig;
};

export { getTailwindConfig };
2 changes: 1 addition & 1 deletion packages/cli/plugin-tailwind/src/design-token/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { lazyImport, createRuntimeExportsUtils } from '@modern-js/utils';
import type { CliPlugin, LegacyAppTools } from '@modern-js/app-tools';
import { DesignSystem } from '../../types';

export default (
export const designTokenPlugin = (
{ pluginName } = { pluginName: '@modern-js/plugin-tailwindcss' },
): CliPlugin<LegacyAppTools> => ({
name: '@modern-js/plugin-design-token',
Expand Down
60 changes: 60 additions & 0 deletions packages/cli/plugin-tailwind/src/macro.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import path from 'path';
import { fs, nanoid, slash } from '@modern-js/utils';

export const template = (configPath: string) => `
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
const modernConfig = _interopRequireDefault(require('${configPath}')).default;

const theme =
modernConfig && modernConfig.source && modernConfig.source.designSystem
? modernConfig.source.designSystem
: {};
const tailwindcss =
modernConfig && modernConfig.tools && modernConfig.tools.tailwindcss
? modernConfig.tools.tailwindcss
: {};

module.exports = {
theme,
...tailwindcss,
};
`;

const TWIN_MACRO_NAME = 'twin.macro';

export const checkTwinMacroExist = async (appDirectory: string) => {
const packageJson =
(await fs.readJSON(path.join(appDirectory, 'package.json'), {
throws: false,
})) || {};

return Boolean(
(typeof packageJson.dependencies === 'object' &&
packageJson.dependencies[TWIN_MACRO_NAME]) ||
(typeof packageJson.devDependencies === 'object' &&
packageJson.devDependencies[TWIN_MACRO_NAME]),
);
};

export const getTwinMacroMajorVersion = (appDirectory: string) => {
try {
const pkgJsonPath = require.resolve(`${TWIN_MACRO_NAME}/package.json`, {
paths: [appDirectory],
});
const { version } = require(pkgJsonPath);
return Number(version.split('.')[0]);
} catch (err) {
return null;
}
};

export const getRandomTwConfigFileName = (internalDirectory: string) => {
return slash(
path.join(
internalDirectory,
`tailwind.config.${Date.now()}.${nanoid()}.js`,
),
);
};
50 changes: 0 additions & 50 deletions packages/cli/plugin-tailwind/src/tailwind.ts

This file was deleted.

10 changes: 6 additions & 4 deletions packages/cli/plugin-tailwind/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export type DesignSystem = Record<string, any> & {
export type DesignSystem = TailwindConfig & {
supportStyledComponents?: boolean;
};

export type Tailwind =
| Record<string, any>
| ((options: Record<string, any>) => Record<string, any> | void);
export type TailwindConfig = Record<string, any>;

export type ExtraTailwindConfig =
| TailwindConfig
| ((options: TailwindConfig) => TailwindConfig | void);
Loading