Skip to content

Commit

Permalink
Enable ESM and TS based config files (#10785)
Browse files Browse the repository at this point in the history
* add `jiti` and `detective-typescript` dependencies

* use `jiti` and `detective-typescript`

Instead of `detective`, this way we will be able to support
`tailwind.config.ts` files and `ESM` files.

* use `@swc/core` instead of the built-in `babel` form `jiti`

* update changelog

* add `jiti` and `detective-typescript` dependencies to `stable`

* use `sucrase` to transform the configs

* add `sucrase` dependency to `stable` engine

* make loading the config easier

* use abstracted loading config utils

* WIP: make `load` related files public API

* use new config loader in PostCSS plugin

* add list of default config files to look for

* cleanup unused arguments

* find default config path when using CLI

* improve `init` command

* make eslint happy

* keep all files in `stubs` folder

* add `tailwind.config.js` stub file

* Initialize PostCSS config using the same format as Tailwind config

* Rename config content stubs to config.*.js

* Improve option descriptions for init options

* Remove unused code, remove `constants` file

* Fix TS warning

* apply CLI changes to the Oxide version

* update `--help` output in CLI tests

* WIP: make tests work on CI

TODO: Test all combinations of `--full`, `--ts`, `--postcss`, and `--esm`.

* wip

* remove unused `fs`

* Fix init tests

Did you know you could pass an empty args to a command? No? Me neither. ¯\_(ツ)_/¯

* bump `napi-derive`

* list extensions we are interested in

* no-op the `removeFile` if file doesn't exist

* ensure all `init` flags work

* ensure we cleanup the new files

* test ESM/CJS generation based on package.json

* remove unnecessary test

We are not displaying output in the `--help` anymore based on whether
`type: module` is present or not.
Therefore this test is unneeded.

* only look for `TypeScript` files when the entryFile is `TypeScript` as well

* refactor `load` to be `loadConfig`

This will allow you to use:

```js
import loadConfig from 'tailwindcss/loadConfig'

let config = loadConfig("/Users/xyz/projects/my-app/tailwind.config.ts")
```

The `loadConfig` function will return the configuration object based on
the given absolute path of a tailwind configuration file.

The given path can be a CJS, an ESM or a TS file.

* use the `config.full.js` stub instead of the `defaultConfig.stub.js` file

The root `defaultConfig` is still there for backwards compatibilty
reasons. But the `module.exports = requrie('./config.full.js')` was
causing some problems when actually using tailwindcss.

So dropped it instead.

* apply `load` -> `loadConfig` changes to `Oxide` engine CLI

* ensure we write the config file in the Oxide engine

* improve type in Oxide engine CLI

* catch errors instead of checking if the file exists

A little smaller but just for tests so doesn't matter too much here 👍

* ensure we publish the correct stub files

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Nate Moore <nate@natemoo.re>
Co-authored-by: Enzo Innocenzi <enzo@innocenzi.dev>
  • Loading branch information
5 people committed Mar 15, 2023
1 parent 694aea0 commit 7e9a53f
Show file tree
Hide file tree
Showing 43 changed files with 1,709 additions and 621 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add `justify-normal` and `justify-stretch` utilities ([#10560](https://github.com/tailwindlabs/tailwindcss/pull/10560))
- Add `content-normal` and `content-stretch` utilities ([#10645](https://github.com/tailwindlabs/tailwindcss/pull/10645))
- Add `line-clamp` utilities from `@tailwindcss/line-clamp` to core ([#10768](https://github.com/tailwindlabs/tailwindcss/pull/10768))
- Enable ESM and TS based config files ([#10785](https://github.com/tailwindlabs/tailwindcss/pull/10785))

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion integrations/execute.js
Expand Up @@ -26,7 +26,7 @@ module.exports = function $(command, options = {}) {
let args = options.shell
? [command]
: (() => {
let args = command.split(' ')
let args = command.trim().split(/\s+/)
command = args.shift()
command =
command === 'node'
Expand Down
6 changes: 4 additions & 2 deletions integrations/io.js
Expand Up @@ -97,10 +97,12 @@ module.exports = function ({
},
async removeFile(file) {
let filePath = path.resolve(toolRoot, file)

if (!fileCache[filePath]) {
fileCache[filePath] = await fs.readFile(filePath, 'utf8')
fileCache[filePath] = await fs.readFile(filePath, 'utf8').catch(() => null)
}
await fs.unlink(filePath)

await fs.unlink(filePath).catch(() => null)
},
async readOutputFile(file) {
file = await resolveFile(file, absoluteOutputFolder)
Expand Down
82 changes: 62 additions & 20 deletions integrations/tailwindcss-cli/tests/cli.test.js
Expand Up @@ -532,6 +532,46 @@ describe('Build command', () => {
})

describe('Init command', () => {
it.each([
{ flags: [], name: 'tailwind.config.js' },
{ flags: ['--ts'], name: 'tailwind.config.ts' },
{ flags: ['--esm'], name: 'tailwind.config.js' },
{ flags: ['--full'], name: 'tailwind.config.js' },
{ flags: ['--ts', '--full'], name: 'tailwind.config.ts' },
{ flags: ['--esm', '--full'], name: 'tailwind.config.js' },
])('works with all these flags: %j', async ({ flags, name }) => {
cleanupFile(name)
await removeFile(name)

let { combined } = await $(`${EXECUTABLE} init ${flags.join(' ')}`)

expect(combined).toMatchInlineSnapshot(`
"
Created Tailwind CSS config file: ${name}
"
`)

expect(await fileExists(name)).toBe(true)

let content = await readOutputFile(`../${name}`)

if (flags.includes('--ts') || flags.includes('--esm')) {
expect(content).toContain('export default')
expect(content).not.toContain('module.exports =')
} else {
expect(content).toContain('module.exports =')
expect(content).not.toContain('export default')
}

if (flags.includes('--ts')) {
expect(content).toContain('satisfies Config')
}

if (flags.includes('--full')) {
expect(content.split('\n').length).toBeGreaterThan(50)
}
})

test('--full', async () => {
cleanupFile('full.config.js')

Expand Down Expand Up @@ -577,14 +617,19 @@ describe('Init command', () => {
tailwindcss init [options]
Options:
-f, --full Initialize a full \`tailwind.config.js\` file
--esm Initialize configuration file as ESM
--ts Initialize configuration file as TypeScript
-p, --postcss Initialize a \`postcss.config.js\` file
-f, --full Include the default values for all options in the generated configuration file
-h, --help Display usage information
`)
)
})

test('--help in ESM package', async () => {
test('ESM config is created by default in an ESM project', async () => {
cleanupFile('tailwind.config.js')
await removeFile('tailwind.config.js')

let pkg = await readOutputFile('../package.json')

await writeInputFile(
Expand All @@ -595,50 +640,47 @@ describe('Init command', () => {
})
)

let { combined } = await $(`${EXECUTABLE} init --help`)
let { combined } = await $(`${EXECUTABLE} init`)

expect(dedent(combined)).toEqual(
dedent(`
tailwindcss v${version}
expect(combined).toMatchInlineSnapshot(`
"
Created Tailwind CSS config file: tailwind.config.js
"
`)

Usage:
tailwindcss init [options]
expect(await fileExists('./tailwind.config.js')).toBe(true)

Options:
-f, --full Initialize a full \`tailwind.config.cjs\` file
-p, --postcss Initialize a \`postcss.config.cjs\` file
-h, --help Display usage information
`)
)
// Not a clean way to test this.
expect(await readOutputFile('../tailwind.config.js')).toContain('export default')

await writeInputFile('../package.json', pkg)
})

test('cjs config created when in ESM package', async () => {
cleanupFile('tailwind.config.cjs')
test('CJS config is created by default in a non-ESM project', async () => {
cleanupFile('tailwind.config.js')
await removeFile('tailwind.config.js')

let pkg = await readOutputFile('../package.json')

await writeInputFile(
'../package.json',
JSON.stringify({
...JSON.parse(pkg),
type: 'module',
})
)

let { combined } = await $(`${EXECUTABLE} init`)

expect(combined).toMatchInlineSnapshot(`
"
Created Tailwind CSS config file: tailwind.config.cjs
Created Tailwind CSS config file: tailwind.config.js
"
`)

expect(await fileExists('./tailwind.config.cjs')).toBe(true)
expect(await fileExists('./tailwind.config.js')).toBe(true)

// Not a clean way to test this.
expect(await readOutputFile('../tailwind.config.cjs')).toContain('module.exports =')
expect(await readOutputFile('../tailwind.config.js')).toContain('module.exports')

await writeInputFile('../package.json', pkg)
})
Expand Down
4 changes: 4 additions & 0 deletions loadConfig.d.ts
@@ -0,0 +1,4 @@
import type { Config } from './types/config'

declare function loadConfig(path: string): Config
export = loadConfig
2 changes: 2 additions & 0 deletions loadConfig.js
@@ -0,0 +1,2 @@
let loadConfig = require('./lib/public/load-config')
module.exports = (loadConfig.__esModule ? loadConfig : { default: loadConfig }).default
21 changes: 15 additions & 6 deletions oxide/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion oxide/crates/node/Cargo.toml
Expand Up @@ -9,7 +9,7 @@ crate-type = ["cdylib"]
[dependencies]
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
napi = { version = "2.10.0", default-features = false, features = ["napi4"] }
napi-derive = "2.9.1"
napi-derive = "2.11.1"
tailwindcss-core = { path = "../core" }
rayon = "1.5.3"

Expand Down

0 comments on commit 7e9a53f

Please sign in to comment.