From d4c30a450685014325a22a687dfc056f3044a3e5 Mon Sep 17 00:00:00 2001 From: Joel Austin Date: Sat, 10 Feb 2024 02:01:35 -0800 Subject: [PATCH] [cli] very rough working stylex cli --- package-lock.json | 102 +++++++++++++--------------------- package.json | 2 +- packages/cli/package.json | 4 +- packages/cli/src/errors.js | 18 ++++++ packages/cli/src/files.js | 57 +++++++++++++++++++ packages/cli/src/index.js | 88 ++++++++++++++++++++++++++++- packages/cli/src/transform.js | 19 ++++++- 7 files changed, 222 insertions(+), 68 deletions(-) create mode 100644 packages/cli/src/errors.js create mode 100644 packages/cli/src/files.js mode change 100644 => 100755 packages/cli/src/index.js diff --git a/package-lock.json b/package-lock.json index 4b6301796..66a85fa29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8151,6 +8151,10 @@ "resolved": "packages/babel-plugin", "link": true }, + "node_modules/@stylexjs/cli": { + "resolved": "packages/cli", + "link": true + }, "node_modules/@stylexjs/dev-runtime": { "resolved": "packages/dev-runtime", "link": true @@ -10291,7 +10295,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, "dependencies": { "node-int64": "^0.4.0" } @@ -13313,7 +13316,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, "dependencies": { "bser": "2.1.1" } @@ -21183,8 +21185,7 @@ "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" }, "node_modules/node-releases": { "version": "2.0.14", @@ -31569,6 +31570,22 @@ "@stylexjs/stylex": "0.5.1" } }, + "packages/cli": { + "name": "@stylexjs/cli", + "version": "0.5.1", + "license": "MIT", + "dependencies": { + "@babel/core": "7.23.9", + "@stylexjs/babel-plugin": "0.5.1", + "fb-watchman": "^2.0.2" + }, + "bin": { + "stylex": "lib/index.js" + }, + "devDependencies": { + "@stylexjs/scripts": "0.5.1" + } + }, "packages/dev-runtime": { "name": "@stylexjs/dev-runtime", "version": "0.5.1", @@ -31636,6 +31653,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -31651,6 +31669,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -31666,6 +31685,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -31681,6 +31701,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -31696,6 +31717,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -31711,6 +31733,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -31726,6 +31749,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -31741,6 +31765,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -31756,6 +31781,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -36601,6 +36627,15 @@ "@stylexjs/stylex": "0.5.1" } }, + "@stylexjs/cli": { + "version": "file:packages/cli", + "requires": { + "@babel/core": "7.23.9", + "@stylexjs/babel-plugin": "0.5.1", + "@stylexjs/scripts": "0.5.1", + "fb-watchman": "^2.0.2" + } + }, "@stylexjs/dev-runtime": { "version": "file:packages/dev-runtime", "requires": { @@ -38391,7 +38426,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, "requires": { "node-int64": "^0.4.0" } @@ -40812,7 +40846,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, "requires": { "bser": "2.1.1" } @@ -46878,8 +46911,7 @@ "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" }, "node-releases": { "version": "2.0.14", @@ -54024,60 +54056,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==" - }, - "@next/swc-darwin-arm64": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.1.tgz", - "integrity": "sha512-JyxnGCS4qT67hdOKQ0CkgFTp+PXub5W1wsGvIq98TNbF3YEIN7iDekYhYsZzc8Ov0pWEsghQt+tANdidITCLaw==", - "optional": true - }, - "@next/swc-darwin-x64": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.1.tgz", - "integrity": "sha512-625Z7bb5AyIzswF9hvfZWa+HTwFZw+Jn3lOBNZB87lUS0iuCYDHqk3ujuHCkiyPtSC0xFBtYDLcrZ11mF/ap3w==", - "optional": true - }, - "@next/swc-linux-arm64-gnu": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.1.tgz", - "integrity": "sha512-iVpn3KG3DprFXzVHM09kvb//4CNNXBQ9NB/pTm8LO+vnnnaObnzFdS5KM+w1okwa32xH0g8EvZIhoB3fI3mS1g==", - "optional": true - }, - "@next/swc-linux-arm64-musl": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.1.tgz", - "integrity": "sha512-mVsGyMxTLWZXyD5sen6kGOTYVOO67lZjLApIj/JsTEEohDDt1im2nkspzfV5MvhfS7diDw6Rp/xvAQaWZTv1Ww==", - "optional": true - }, - "@next/swc-linux-x64-gnu": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.1.tgz", - "integrity": "sha512-wMqf90uDWN001NqCM/auRl3+qVVeKfjJdT9XW+RMIOf+rhUzadmYJu++tp2y+hUbb6GTRhT+VjQzcgg/QTD9NQ==", - "optional": true - }, - "@next/swc-linux-x64-musl": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.1.tgz", - "integrity": "sha512-ol1X1e24w4j4QwdeNjfX0f+Nza25n+ymY0T2frTyalVczUmzkVD7QGgPTZMHfR1aLrO69hBs0G3QBYaj22J5GQ==", - "optional": true - }, - "@next/swc-win32-arm64-msvc": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.1.tgz", - "integrity": "sha512-WEmTEeWs6yRUEnUlahTgvZteh5RJc4sEjCQIodJlZZ5/VJwVP8p2L7l6VhzQhT4h7KvLx/Ed4UViBdne6zpIsw==", - "optional": true - }, - "@next/swc-win32-ia32-msvc": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.1.tgz", - "integrity": "sha512-oFpHphN4ygAgZUKjzga7SoH2VGbEJXZa/KL8bHCAwCjDWle6R1SpiGOdUdA8EJ9YsG1TYWpzY6FTbUA+iAJeww==", - "optional": true - }, - "@next/swc-win32-x64-msvc": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.1.tgz", - "integrity": "sha512-FFp3nOJ/5qSpeWT0BZQ+YE1pSMk4IMpkME/1DwKBwhg4mJLB9L+6EXuJi4JEwaJdl5iN+UUlmUD3IsR1kx5fAg==", - "optional": true } } } diff --git a/package.json b/package.json index 9738475bd..319b7c689 100644 --- a/package.json +++ b/package.json @@ -41,8 +41,8 @@ "@types/jest": "^29.5.11", "babel-plugin-syntax-hermes-parser": "^0.18.2", "benchmark": "^2.1.4", - "esbuild": "^0.19.12", "cross-env": "^7.0.3", + "esbuild": "^0.19.12", "eslint": "8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-ft-flow": "^3.0.2", diff --git a/packages/cli/package.json b/packages/cli/package.json index 128a3ed46..3d2df8188 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -2,7 +2,7 @@ "name": "@stylexjs/cli", "version": "0.5.1", "description": "A cli to compile a folder with stylex", - "main": "./lib/index.js", + "main": "./src/index.js", "repository": "https://www.github.com/facebook/stylex", "license": "MIT", "scripts": { @@ -23,7 +23,7 @@ "@stylexjs/scripts": "0.5.1" }, "bin": { - "stylex": "./lib/index.js" + "stylex": "./src/index.js" }, "jest": {}, "files": [ diff --git a/packages/cli/src/errors.js b/packages/cli/src/errors.js new file mode 100644 index 000000000..426ec0df3 --- /dev/null +++ b/packages/cli/src/errors.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + */ + +type Errors = $ReadOnly<{ + dirNotFound: Error, +}>; + +const errors: Errors = { + dirNotFound: new Error('Invalid Directory: Not Found'), +}; + +export default errors; diff --git a/packages/cli/src/files.js b/packages/cli/src/files.js new file mode 100644 index 000000000..c304ccd55 --- /dev/null +++ b/packages/cli/src/files.js @@ -0,0 +1,57 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + */ + +// import type { Rule } from '@stylexjs/babel-plugin'; + +import fs from 'fs'; +import path from 'path'; +import errors from './errors'; + +// gets the directory for compiled styles (creates it if it doesn't exist) +export function makeCompiledDir(compiledDir: string): void { + if (!fs.existsSync(compiledDir)) { + console.log('made dir', compiledDir); + fs.mkdirSync(compiledDir); + } +} + +export function getInputDirectoryFiles(inputDir: string): Array { + if (!fs.existsSync(inputDir)) { + throw errors.dirNotFound; + } else { + const files = fs.readdirSync(inputDir, { recursive: true }); + return files.filter((file) => { + const maybeDir = path.join(inputDir, file); + return !isDir(maybeDir); + }); + } +} + +// takes in the compiled rules and writes them to a file at the top of the compiled directory +export function writeCompiledCSS(filePath: string, compiledCSS: string): void { + fs.writeFileSync(filePath, compiledCSS); +} + +export function writeCompiledJS(path: string, code: string): void { + makeDirExistRecursive(path); + fs.writeFileSync(path, code, {}); +} + +export function isDir(path: string): boolean { + return fs.lstatSync(path).isDirectory(); +} + +function makeDirExistRecursive(filePath: string): ?boolean { + const dirName = path.dirname(filePath); + if (fs.existsSync(dirName)) { + return true; + } + makeDirExistRecursive(dirName); + fs.mkdirSync(dirName); +} diff --git a/packages/cli/src/index.js b/packages/cli/src/index.js old mode 100644 new mode 100755 index 27880bb5f..f825d2963 --- a/packages/cli/src/index.js +++ b/packages/cli/src/index.js @@ -1,3 +1,4 @@ +#! /usr/bin/env node /** * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -7,4 +8,89 @@ * @flow strict */ -import watchman from 'fb-watchman'; +// import watchman from 'fb-watchman'; +import { + getInputDirectoryFiles, + isDir, + makeCompiledDir, + writeCompiledCSS, + writeCompiledJS, +} from './files'; +import { compileRules, transformFile } from './transform'; +import yargs from 'yargs'; + +import path from 'path'; + +import type { Rule } from '@stylexjs/babel-plugin'; +import errors from './errors'; + +// Rough list of steps + +// Walk through files +// Compile all of them with stylex babel plugin +// When using babel plugin with transform/transformSync, +// Has metadata key/object, which may have stylex key +// Stylex object has styles generated in each file +// Pass them to processCSS in function and write return to output +// Import css in every file relative to file path + +// make watchman look for directory changes +// create cache from time changed on files and recompile only those files? + +// Accept list of node_modules files to ALSO compile and include in the css +// Make a subfolder inside of output of compiled_modules +// Can skip injecting css import in compiled_modules + +type StyleXRules = Array; + +const usage = + '\n Usage: provide a directory to stylex in order to have it compiled.'; +const _options = yargs + .usage(usage) + .option('d', { + alias: 'directory', + describe: 'The directory to compile with Stylex', + type: 'string', + demandOption: true, + }) + .help(true).argsv; + +const dir: string = yargs.argv.directory; +if (!isDir(dir)) { + throw errors.dirNotFound; +} + +export const INPUT_DIR: string = path.normalize(dir); + +const inputParent: string = path.dirname(INPUT_DIR); + +export const COMPILED_DIR: string = path.join(inputParent, 'src'); +export const CSS_FILE_PATH: string = path.join( + COMPILED_DIR, + 'stylex-bundle.css', +); + +makeCompiledDir(COMPILED_DIR); +const allStyleXRules: StyleXRules = []; +const compiledJS = new Map(); + +// without watch flag +compileDirectory(dir); + +async function compileDirectory(dir: string) { + const dirFiles = getInputDirectoryFiles(dir); + for (const file of dirFiles) { + await compileFile(file); + } + const compiledCSS = await compileRules(allStyleXRules); + writeCompiledCSS(CSS_FILE_PATH, compiledCSS); +} + +async function compileFile(filePath: string) { + const [code, rules] = await transformFile(dir, path.join(dir, filePath)); + if (code != null) { + compiledJS.set(filePath, code); + allStyleXRules.push(...rules); + writeCompiledJS(path.join(COMPILED_DIR, filePath), code); + } +} diff --git a/packages/cli/src/transform.js b/packages/cli/src/transform.js index 1d5aeb72a..4b1d71cac 100644 --- a/packages/cli/src/transform.js +++ b/packages/cli/src/transform.js @@ -8,8 +8,23 @@ */ import * as babel from '@babel/core'; +import styleXPlugin from '@stylexjs/babel-plugin'; +import type { Rule } from '@stylexjs/babel-plugin'; -export async function transformFile(filename: string): Promise<> { - const result = await babel.transformFileAsync(filename, {}); +export async function transformFile( + dir: string, + fileName: string, +): Promise<[?string, Array]> { + const result = await babel.transformFileAsync(fileName, { + plugins: [styleXPlugin], + }); + // $FlowFixMe const { code, metadata } = result; + // $FlowFixMe + const styleXRules: Array = metadata.stylex; + return [code, styleXRules]; +} + +export async function compileRules(rules: Array): Promise { + return styleXPlugin.processStylexRules(rules); }