tailwindcss 0.0.0-insiders.c5ca373 → 0.0.0-insiders.c5eef09

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +7 -6
  2. package/lib/cli/build/index.js +5 -1
  3. package/lib/cli/build/plugin.js +84 -34
  4. package/lib/cli/build/watching.js +77 -12
  5. package/lib/cli/index.js +231 -10
  6. package/lib/cli/init/index.js +2 -2
  7. package/lib/cli.js +4 -226
  8. package/lib/corePlugins.js +344 -31
  9. package/lib/css/preflight.css +2 -0
  10. package/lib/featureFlags.js +12 -10
  11. package/lib/index.js +4 -46
  12. package/lib/lib/collapseAdjacentRules.js +2 -2
  13. package/lib/lib/collapseDuplicateDeclarations.js +2 -2
  14. package/lib/lib/content.js +24 -9
  15. package/lib/lib/defaultExtractor.js +15 -5
  16. package/lib/lib/detectNesting.js +7 -1
  17. package/lib/lib/evaluateTailwindFunctions.js +4 -4
  18. package/lib/lib/expandApplyAtRules.js +36 -17
  19. package/lib/lib/expandTailwindAtRules.js +36 -10
  20. package/lib/lib/findAtConfigPath.js +7 -7
  21. package/lib/lib/generateRules.js +147 -66
  22. package/lib/lib/offsets.js +90 -2
  23. package/lib/lib/remap-bitfield.js +87 -0
  24. package/lib/lib/resolveDefaultsAtRules.js +4 -4
  25. package/lib/lib/setupContextUtils.js +286 -79
  26. package/lib/lib/setupTrackingContext.js +25 -4
  27. package/lib/lib/sharedState.js +21 -1
  28. package/lib/oxide/cli/build/deps.js +81 -0
  29. package/lib/oxide/cli/build/index.js +47 -0
  30. package/lib/oxide/cli/build/plugin.js +364 -0
  31. package/lib/oxide/cli/build/utils.js +77 -0
  32. package/lib/oxide/cli/build/watching.js +177 -0
  33. package/lib/oxide/cli/help/index.js +70 -0
  34. package/lib/oxide/cli/index.js +220 -0
  35. package/lib/oxide/cli/init/index.js +35 -0
  36. package/lib/oxide/cli.js +5 -0
  37. package/lib/oxide/postcss-plugin.js +2 -0
  38. package/lib/plugin.js +98 -0
  39. package/lib/postcss-plugins/nesting/plugin.js +2 -2
  40. package/lib/util/buildMediaQuery.js +5 -2
  41. package/lib/util/cloneNodes.js +2 -2
  42. package/lib/util/color.js +4 -4
  43. package/lib/util/createUtilityPlugin.js +2 -2
  44. package/lib/util/dataTypes.js +29 -2
  45. package/lib/util/defaults.js +4 -4
  46. package/lib/util/escapeClassName.js +3 -3
  47. package/lib/util/formatVariantSelector.js +173 -107
  48. package/lib/util/getAllConfigs.js +2 -2
  49. package/lib/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +2 -2
  50. package/lib/util/nameClass.js +3 -0
  51. package/lib/util/negateValue.js +2 -2
  52. package/lib/util/normalizeConfig.js +36 -22
  53. package/lib/util/normalizeScreens.js +100 -3
  54. package/lib/util/pluginUtils.js +107 -22
  55. package/lib/util/prefixSelector.js +22 -8
  56. package/lib/util/resolveConfig.js +11 -16
  57. package/oxide-node-api-shim/index.js +21 -0
  58. package/oxide-node-api-shim/package.json +5 -0
  59. package/package.json +38 -24
  60. package/peers/index.js +790 -765
  61. package/resolveConfig.d.ts +11 -2
  62. package/scripts/release-channel.js +18 -0
  63. package/scripts/release-notes.js +21 -0
  64. package/scripts/swap-engines.js +40 -0
  65. package/src/cli/build/index.js +6 -2
  66. package/src/cli/build/plugin.js +82 -15
  67. package/src/cli/build/watching.js +105 -12
  68. package/src/cli/index.js +234 -3
  69. package/src/cli.js +4 -220
  70. package/src/corePlugins.js +283 -23
  71. package/src/css/preflight.css +2 -0
  72. package/src/featureFlags.js +2 -0
  73. package/src/index.js +4 -46
  74. package/src/lib/content.js +21 -12
  75. package/src/lib/defaultExtractor.js +12 -3
  76. package/src/lib/detectNesting.js +9 -1
  77. package/src/lib/expandApplyAtRules.js +35 -15
  78. package/src/lib/expandTailwindAtRules.js +37 -6
  79. package/src/lib/findAtConfigPath.js +4 -6
  80. package/src/lib/generateRules.js +152 -55
  81. package/src/lib/offsets.js +110 -3
  82. package/src/lib/remap-bitfield.js +82 -0
  83. package/src/lib/setupContextUtils.js +315 -60
  84. package/src/lib/setupTrackingContext.js +31 -6
  85. package/src/lib/sharedState.js +19 -0
  86. package/src/oxide/cli/build/deps.ts +91 -0
  87. package/src/oxide/cli/build/index.ts +47 -0
  88. package/src/oxide/cli/build/plugin.ts +436 -0
  89. package/src/oxide/cli/build/utils.ts +74 -0
  90. package/src/oxide/cli/build/watching.ts +225 -0
  91. package/src/oxide/cli/help/index.ts +69 -0
  92. package/src/oxide/cli/index.ts +212 -0
  93. package/src/oxide/cli/init/index.ts +32 -0
  94. package/src/oxide/cli.ts +1 -0
  95. package/src/oxide/postcss-plugin.ts +1 -0
  96. package/src/plugin.js +107 -0
  97. package/src/util/buildMediaQuery.js +5 -3
  98. package/src/util/dataTypes.js +33 -4
  99. package/src/util/formatVariantSelector.js +217 -124
  100. package/src/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +1 -1
  101. package/src/util/nameClass.js +4 -0
  102. package/src/util/negateValue.js +1 -1
  103. package/src/util/normalizeConfig.js +18 -0
  104. package/src/util/normalizeScreens.js +99 -4
  105. package/src/util/pluginUtils.js +116 -8
  106. package/src/util/prefixSelector.js +28 -10
  107. package/src/util/resolveConfig.js +3 -9
  108. package/stubs/defaultConfig.stub.js +162 -166
  109. package/types/config.d.ts +37 -10
  110. package/types/generated/default-theme.d.ts +92 -78
  111. package/CHANGELOG.md +0 -2269
  112. package/lib/cli/shared.js +0 -12
  113. package/scripts/install-integrations.js +0 -27
  114. package/scripts/rebuildFixtures.js +0 -68
  115. package/src/cli/shared.js +0 -5
package/README.md CHANGED
@@ -1,9 +1,10 @@
1
1
  <p align="center">
2
- <a href="https://tailwindcss.com/#gh-light-mode-only" target="_blank">
3
- <img src="./.github/logo-light.svg" alt="Tailwind CSS" width="350" height="70">
4
- </a>
5
- <a href="https://tailwindcss.com/#gh-dark-mode-only" target="_blank">
6
- <img src="./.github/logo-dark.svg" alt="Tailwind CSS" width="350" height="70">
2
+ <a href="https://tailwindcss.com" target="_blank">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/tailwindlabs/tailwindcss/HEAD/.github/logo-dark.svg">
5
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/tailwindlabs/tailwindcss/HEAD/.github/logo-light.svg">
6
+ <img alt="Tailwind CSS" src="https://raw.githubusercontent.com/tailwindlabs/tailwindcss/HEAD/.github/logo-light.svg" width="350" height="70" style="max-width: 100%;">
7
+ </picture>
7
8
  </a>
8
9
  </p>
9
10
 
@@ -13,7 +14,7 @@
13
14
 
14
15
 
15
16
  <p align="center">
16
- <a href="https://github.com/tailwindlabs/tailwindcss/actions"><img src="https://img.shields.io/github/workflow/status/tailwindlabs/tailwindcss/Node.js%20CI" alt="Build Status"></a>
17
+ <a href="https://github.com/tailwindlabs/tailwindcss/actions"><img src="https://img.shields.io/github/actions/workflow/status/tailwindlabs/tailwindcss/ci-stable.yml?branch=master" alt="Build Status"></a>
17
18
  <a href="https://www.npmjs.com/package/tailwindcss"><img src="https://img.shields.io/npm/dt/tailwindcss.svg" alt="Total Downloads"></a>
18
19
  <a href="https://github.com/tailwindcss/tailwindcss/releases"><img src="https://img.shields.io/npm/v/tailwindcss.svg" alt="Latest Release"></a>
19
20
  <a href="https://github.com/tailwindcss/tailwindcss/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/tailwindcss.svg" alt="License"></a>
@@ -35,7 +35,11 @@ async function build(args, configs) {
35
35
  let configPath = args["--config"] ? args["--config"] : ((defaultPath)=>_fs.default.existsSync(defaultPath) ? defaultPath : null)(_path.default.resolve(`./${configs.tailwind}`));
36
36
  let processor = await (0, _pluginJs.createProcessor)(args, configPath);
37
37
  if (shouldWatch) {
38
- /* Abort the watcher if stdin is closed to avoid zombie processes */ process.stdin.on("end", ()=>process.exit(0));
38
+ // Abort the watcher if stdin is closed to avoid zombie processes
39
+ // You can disable this behavior with --watch=always
40
+ if (args["--watch"] !== "always") {
41
+ process.stdin.on("end", ()=>process.exit(0));
42
+ }
39
43
  process.stdin.resume();
40
44
  await processor.watch();
41
45
  } else {
@@ -18,13 +18,14 @@ const _options = /*#__PURE__*/ _interopRequireDefault(require("postcss-load-conf
18
18
  const _processTailwindFeatures = /*#__PURE__*/ _interopRequireDefault(require("../../processTailwindFeatures"));
19
19
  const _deps = require("./deps");
20
20
  const _utils = require("./utils");
21
- const _shared = require("../shared");
21
+ const _sharedState = require("../../lib/sharedState");
22
22
  const _resolveConfigJs = /*#__PURE__*/ _interopRequireDefault(require("../../../resolveConfig.js"));
23
23
  const _getModuleDependenciesJs = /*#__PURE__*/ _interopRequireDefault(require("../../lib/getModuleDependencies.js"));
24
24
  const _contentJs = require("../../lib/content.js");
25
25
  const _watchingJs = require("./watching.js");
26
26
  const _fastGlob = /*#__PURE__*/ _interopRequireDefault(require("fast-glob"));
27
27
  const _findAtConfigPathJs = require("../../lib/findAtConfigPath.js");
28
+ const _log = /*#__PURE__*/ _interopRequireDefault(require("../../util/log"));
28
29
  function _interopRequireDefault(obj) {
29
30
  return obj && obj.__esModule ? obj : {
30
31
  default: obj
@@ -112,8 +113,8 @@ let state = {
112
113
  contextDependencies: new Set(),
113
114
  /** @type {import('../../lib/content.js').ContentPath[]} */ contentPaths: [],
114
115
  refreshContentPaths () {
115
- var ref;
116
- this.contentPaths = (0, _contentJs.parseCandidateFiles)(this.context, (ref = this.context) === null || ref === void 0 ? void 0 : ref.tailwindConfig);
116
+ var _this_context;
117
+ this.contentPaths = (0, _contentJs.parseCandidateFiles)(this.context, (_this_context = this.context) === null || _this_context === void 0 ? void 0 : _this_context.tailwindConfig);
117
118
  },
118
119
  get config () {
119
120
  return this.context.tailwindConfig;
@@ -124,7 +125,7 @@ let state = {
124
125
  dynamic: this.contentPaths.filter((contentPath)=>contentPath.glob !== undefined).map((contentPath)=>contentPath.pattern)
125
126
  };
126
127
  },
127
- loadConfig (configPath) {
128
+ loadConfig (configPath, content) {
128
129
  if (this.watcher && configPath) {
129
130
  this.refreshConfigDependencies(configPath);
130
131
  }
@@ -135,10 +136,14 @@ let state = {
135
136
  files: []
136
137
  }
137
138
  });
139
+ // Override content files if `--content` has been passed explicitly
140
+ if ((content === null || content === void 0 ? void 0 : content.length) > 0) {
141
+ config.content.files = content;
142
+ }
138
143
  return config;
139
144
  },
140
145
  refreshConfigDependencies (configPath) {
141
- _shared.env.DEBUG && console.time("Module dependencies");
146
+ _sharedState.env.DEBUG && console.time("Module dependencies");
142
147
  for (let file of this.configDependencies){
143
148
  delete require.cache[require.resolve(file)];
144
149
  }
@@ -148,7 +153,7 @@ let state = {
148
153
  this.configDependencies.add(dependency);
149
154
  }
150
155
  }
151
- _shared.env.DEBUG && console.timeEnd("Module dependencies");
156
+ _sharedState.env.DEBUG && console.timeEnd("Module dependencies");
152
157
  },
153
158
  readContentPaths () {
154
159
  let content = [];
@@ -156,69 +161,86 @@ let state = {
156
161
  // TODO: When we make the postcss plugin async-capable this can become async
157
162
  let files = _fastGlob.default.sync(this.contentPatterns.all);
158
163
  for (let file of files){
159
- content.push({
160
- content: _fs.default.readFileSync(_path.default.resolve(file), "utf8"),
161
- extension: _path.default.extname(file).slice(1)
162
- });
164
+ if (_sharedState.env.OXIDE) {
165
+ content.push({
166
+ file,
167
+ extension: _path.default.extname(file).slice(1)
168
+ });
169
+ } else {
170
+ content.push({
171
+ content: _fs.default.readFileSync(_path.default.resolve(file), "utf8"),
172
+ extension: _path.default.extname(file).slice(1)
173
+ });
174
+ }
163
175
  }
164
176
  // Resolve raw content in the tailwind config
165
177
  let rawContent = this.config.content.files.filter((file)=>{
166
178
  return file !== null && typeof file === "object";
167
179
  });
168
- for (let { raw: content1 , extension ="html" } of rawContent){
169
- content1.push({
170
- content: content1,
180
+ for (let { raw: htmlContent , extension ="html" } of rawContent){
181
+ content.push({
182
+ content: htmlContent,
171
183
  extension
172
184
  });
173
185
  }
174
186
  return content;
175
187
  },
176
- getContext ({ createContext , cliConfigPath , root , result }) {
188
+ getContext ({ createContext , cliConfigPath , root , result , content }) {
177
189
  if (this.context) {
178
190
  this.context.changedContent = this.changedContent.splice(0);
179
191
  return this.context;
180
192
  }
181
- _shared.env.DEBUG && console.time("Searching for config");
182
- var ref;
183
- let configPath = (ref = (0, _findAtConfigPathJs.findAtConfigPath)(root, result)) !== null && ref !== void 0 ? ref : cliConfigPath;
184
- _shared.env.DEBUG && console.timeEnd("Searching for config");
185
- _shared.env.DEBUG && console.time("Loading config");
186
- let config = this.loadConfig(configPath);
187
- _shared.env.DEBUG && console.timeEnd("Loading config");
188
- _shared.env.DEBUG && console.time("Creating context");
193
+ _sharedState.env.DEBUG && console.time("Searching for config");
194
+ var _findAtConfigPath;
195
+ let configPath = (_findAtConfigPath = (0, _findAtConfigPathJs.findAtConfigPath)(root, result)) !== null && _findAtConfigPath !== void 0 ? _findAtConfigPath : cliConfigPath;
196
+ _sharedState.env.DEBUG && console.timeEnd("Searching for config");
197
+ _sharedState.env.DEBUG && console.time("Loading config");
198
+ let config = this.loadConfig(configPath, content);
199
+ _sharedState.env.DEBUG && console.timeEnd("Loading config");
200
+ _sharedState.env.DEBUG && console.time("Creating context");
189
201
  this.context = createContext(config, []);
190
202
  Object.assign(this.context, {
191
203
  userConfigPath: configPath
192
204
  });
193
- _shared.env.DEBUG && console.timeEnd("Creating context");
194
- _shared.env.DEBUG && console.time("Resolving content paths");
205
+ _sharedState.env.DEBUG && console.timeEnd("Creating context");
206
+ _sharedState.env.DEBUG && console.time("Resolving content paths");
195
207
  this.refreshContentPaths();
196
- _shared.env.DEBUG && console.timeEnd("Resolving content paths");
208
+ _sharedState.env.DEBUG && console.timeEnd("Resolving content paths");
197
209
  if (this.watcher) {
198
- _shared.env.DEBUG && console.time("Watch new files");
210
+ _sharedState.env.DEBUG && console.time("Watch new files");
199
211
  this.watcher.refreshWatchedFiles();
200
- _shared.env.DEBUG && console.timeEnd("Watch new files");
212
+ _sharedState.env.DEBUG && console.timeEnd("Watch new files");
201
213
  }
202
- _shared.env.DEBUG && console.time("Reading content files");
203
214
  for (let file of this.readContentPaths()){
204
215
  this.context.changedContent.push(file);
205
216
  }
206
- _shared.env.DEBUG && console.timeEnd("Reading content files");
207
217
  return this.context;
208
218
  }
209
219
  };
210
220
  async function createProcessor(args, cliConfigPath) {
221
+ var _args_content;
211
222
  let postcss = (0, _deps.loadPostcss)();
212
223
  let input = args["--input"];
213
224
  let output = args["--output"];
214
225
  let includePostCss = args["--postcss"];
215
226
  let customPostCssPath = typeof args["--postcss"] === "string" ? args["--postcss"] : undefined;
216
227
  let [beforePlugins, afterPlugins, postcssOptions] = includePostCss ? await loadPostCssPlugins(customPostCssPath) : loadBuiltinPostcssPlugins();
228
+ if (args["--purge"]) {
229
+ _log.default.warn("purge-flag-deprecated", [
230
+ "The `--purge` flag has been deprecated.",
231
+ "Please use `--content` instead."
232
+ ]);
233
+ if (!args["--content"]) {
234
+ args["--content"] = args["--purge"];
235
+ }
236
+ }
237
+ var _args_content_split;
238
+ let content = (_args_content_split = (_args_content = args["--content"]) === null || _args_content === void 0 ? void 0 : _args_content.split(/(?<!{[^}]+),/)) !== null && _args_content_split !== void 0 ? _args_content_split : [];
217
239
  let tailwindPlugin = ()=>{
218
240
  return {
219
241
  postcssPlugin: "tailwindcss",
220
242
  Once (root, { result }) {
221
- _shared.env.DEBUG && console.time("Compiling CSS");
243
+ _sharedState.env.DEBUG && console.time("Compiling CSS");
222
244
  (0, _processTailwindFeatures.default)(({ createContext })=>{
223
245
  console.error();
224
246
  console.error("Rebuilding...");
@@ -227,11 +249,12 @@ async function createProcessor(args, cliConfigPath) {
227
249
  createContext,
228
250
  cliConfigPath,
229
251
  root,
230
- result
252
+ result,
253
+ content
231
254
  });
232
255
  };
233
256
  })(root, result);
234
- _shared.env.DEBUG && console.timeEnd("Compiling CSS");
257
+ _sharedState.env.DEBUG && console.timeEnd("Compiling CSS");
235
258
  }
236
259
  };
237
260
  };
@@ -265,18 +288,45 @@ async function createProcessor(args, cliConfigPath) {
265
288
  from: input,
266
289
  to: output
267
290
  })).then((result)=>{
291
+ if (!state.watcher) {
292
+ return result;
293
+ }
294
+ _sharedState.env.DEBUG && console.time("Recording PostCSS dependencies");
295
+ for (let message of result.messages){
296
+ if (message.type === "dependency") {
297
+ state.contextDependencies.add(message.file);
298
+ }
299
+ }
300
+ _sharedState.env.DEBUG && console.timeEnd("Recording PostCSS dependencies");
301
+ // TODO: This needs to be in a different spot
302
+ _sharedState.env.DEBUG && console.time("Watch new files");
303
+ state.watcher.refreshWatchedFiles();
304
+ _sharedState.env.DEBUG && console.timeEnd("Watch new files");
305
+ return result;
306
+ }).then((result)=>{
268
307
  if (!output) {
269
308
  process.stdout.write(result.css);
270
309
  return;
271
310
  }
272
311
  return Promise.all([
273
- (0, _utils.outputFile)(output, result.css),
274
- result.map && (0, _utils.outputFile)(output + ".map", result.map.toString())
312
+ (0, _utils.outputFile)(result.opts.to, result.css),
313
+ result.map && (0, _utils.outputFile)(result.opts.to + ".map", result.map.toString())
275
314
  ]);
276
315
  }).then(()=>{
277
316
  let end = process.hrtime.bigint();
278
317
  console.error();
279
318
  console.error("Done in", (end - start) / BigInt(1e6) + "ms.");
319
+ }).then(()=>{}, (err)=>{
320
+ // TODO: If an initial build fails we can't easily pick up any PostCSS dependencies
321
+ // that were collected before the error occurred
322
+ // The result is not stored on the error so we have to store it externally
323
+ // and pull the messages off of it here somehow
324
+ // This results in a less than ideal DX because the watcher will not pick up
325
+ // changes to imported CSS if one of them caused an error during the initial build
326
+ // If you fix it and then save the main CSS file so there's no error
327
+ // The watcher will start watching the imported CSS files and will be
328
+ // resilient to future errors.
329
+ console.error(err);
280
330
  });
281
331
  }
282
332
  /**
@@ -37,22 +37,70 @@ function createWatcher(args, { state , rebuild }) {
37
37
  pollInterval: pollInterval
38
38
  } : false
39
39
  });
40
+ // A queue of rebuilds, file reads, etc… to run
40
41
  let chain = Promise.resolve();
41
- let pendingRebuilds = new Set();
42
- let changedContent = [];
42
+ /**
43
+ * A list of files that have been changed since the last rebuild
44
+ *
45
+ * @type {{file: string, content: () => Promise<string>, extension: string}[]}
46
+ */ let changedContent = [];
47
+ /**
48
+ * A list of files for which a rebuild has already been queued.
49
+ * This is used to prevent duplicate rebuilds when multiple events are fired for the same file.
50
+ * The rebuilt file is cleared from this list when it's associated rebuild has _started_
51
+ * This is because if the file is changed during a rebuild it won't trigger a new rebuild which it should
52
+ **/ let pendingRebuilds = new Set();
53
+ let _timer;
54
+ let _reject;
55
+ /**
56
+ * Rebuilds the changed files and resolves when the rebuild is
57
+ * complete regardless of whether it was successful or not
58
+ */ async function rebuildAndContinue() {
59
+ let changes = changedContent.splice(0);
60
+ // There are no changes to rebuild so we can just do nothing
61
+ if (changes.length === 0) {
62
+ return Promise.resolve();
63
+ }
64
+ // Clear all pending rebuilds for the about-to-be-built files
65
+ changes.forEach((change)=>pendingRebuilds.delete(change.file));
66
+ // Resolve the promise even when the rebuild fails
67
+ return rebuild(changes).then(()=>{}, ()=>{});
68
+ }
43
69
  /**
44
70
  *
45
71
  * @param {*} file
46
72
  * @param {(() => Promise<string>) | null} content
47
- */ function recordChangedFile(file, content = null) {
73
+ * @param {boolean} skipPendingCheck
74
+ * @returns {Promise<void>}
75
+ */ function recordChangedFile(file, content = null, skipPendingCheck = false) {
48
76
  file = _path.default.resolve(file);
49
- content = content !== null && content !== void 0 ? content : async ()=>await _fs.default.promises.readFile(file, "utf8");
77
+ // Applications like Vim/Neovim fire both rename and change events in succession for atomic writes
78
+ // In that case rebuild has already been queued by rename, so can be skipped in change
79
+ if (pendingRebuilds.has(file) && !skipPendingCheck) {
80
+ return Promise.resolve();
81
+ }
82
+ // Mark that a rebuild of this file is going to happen
83
+ // It MUST happen synchronously before the rebuild is queued for this to be effective
84
+ pendingRebuilds.add(file);
50
85
  changedContent.push({
51
86
  file,
52
- content,
87
+ content: content !== null && content !== void 0 ? content : ()=>_fs.default.promises.readFile(file, "utf8"),
53
88
  extension: _path.default.extname(file).slice(1)
54
89
  });
55
- chain = chain.then(()=>rebuild(changedContent));
90
+ if (_timer) {
91
+ clearTimeout(_timer);
92
+ _reject();
93
+ }
94
+ // If a rebuild is already in progress we don't want to start another one until the 10ms timer has expired
95
+ chain = chain.then(()=>new Promise((resolve, reject)=>{
96
+ _timer = setTimeout(resolve, 10);
97
+ _reject = reject;
98
+ }));
99
+ // Resolves once this file has been rebuilt (or the rebuild for this file has failed)
100
+ // This queues as many rebuilds as there are changed files
101
+ // But those rebuilds happen after some delay
102
+ // And will immediately resolve if there are no changes
103
+ chain = chain.then(rebuildAndContinue, rebuildAndContinue);
56
104
  return chain;
57
105
  }
58
106
  watcher.on("change", (file)=>recordChangedFile(file));
@@ -91,15 +139,32 @@ function createWatcher(args, { state , rebuild }) {
91
139
  if (pendingRebuilds.has(filePath)) {
92
140
  return;
93
141
  }
142
+ // We'll go ahead and add the file to the pending rebuilds list here
143
+ // It'll be removed when the rebuild starts unless the read fails
144
+ // which will be taken care of as well
94
145
  pendingRebuilds.add(filePath);
95
- chain = chain.then(async ()=>{
96
- let content;
146
+ async function enqueue() {
97
147
  try {
98
- content = await (0, _utilsJs.readFileWithRetries)(_path.default.resolve(filePath));
99
- } finally{
100
- pendingRebuilds.delete(filePath);
148
+ // We need to read the file as early as possible outside of the chain
149
+ // because it may be gone by the time we get to it. doing the read
150
+ // immediately increases the chance that the file is still there
151
+ let content = await (0, _utilsJs.readFileWithRetries)(_path.default.resolve(filePath));
152
+ if (content === undefined) {
153
+ return;
154
+ }
155
+ // This will push the rebuild onto the chain
156
+ // We MUST skip the rebuild check here otherwise the rebuild will never happen on Linux
157
+ // This is because the order of events and timing is different on Linux
158
+ // @ts-ignore: TypeScript isn't picking up that content is a string here
159
+ await recordChangedFile(filePath, ()=>content, true);
160
+ } catch {
161
+ // If reading the file fails, it's was probably a deleted temporary file
162
+ // So we can ignore it and no rebuild is needed
101
163
  }
102
- return recordChangedFile(filePath, ()=>content);
164
+ }
165
+ enqueue().then(()=>{
166
+ // If the file read fails we still need to make sure the file isn't stuck in the pending rebuilds list
167
+ pendingRebuilds.delete(filePath);
103
168
  });
104
169
  });
105
170
  return {
package/lib/cli/index.js CHANGED
@@ -1,18 +1,239 @@
1
+ #!/usr/bin/env node
1
2
  "use strict";
2
3
  Object.defineProperty(exports, "__esModule", {
3
4
  value: true
4
5
  });
5
- _exportStar(require("./build"), exports);
6
- _exportStar(require("./config"), exports);
7
- _exportStar(require("./content"), exports);
8
- function _exportStar(from, to) {
9
- Object.keys(from).forEach(function(k) {
10
- if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) Object.defineProperty(to, k, {
11
- enumerable: true,
12
- get: function() {
13
- return from[k];
6
+ const _path = /*#__PURE__*/ _interopRequireDefault(require("path"));
7
+ const _arg = /*#__PURE__*/ _interopRequireDefault(require("arg"));
8
+ const _fs = /*#__PURE__*/ _interopRequireDefault(require("fs"));
9
+ const _build = require("./build");
10
+ const _help = require("./help");
11
+ const _init = require("./init");
12
+ function _interopRequireDefault(obj) {
13
+ return obj && obj.__esModule ? obj : {
14
+ default: obj
15
+ };
16
+ }
17
+ function isESM() {
18
+ const pkgPath = _path.default.resolve("./package.json");
19
+ try {
20
+ let pkg = JSON.parse(_fs.default.readFileSync(pkgPath, "utf8"));
21
+ return pkg.type && pkg.type === "module";
22
+ } catch (err) {
23
+ return false;
24
+ }
25
+ }
26
+ let configs = isESM() ? {
27
+ tailwind: "tailwind.config.cjs",
28
+ postcss: "postcss.config.cjs"
29
+ } : {
30
+ tailwind: "tailwind.config.js",
31
+ postcss: "postcss.config.js"
32
+ };
33
+ // ---
34
+ function oneOf(...options) {
35
+ return Object.assign((value = true)=>{
36
+ for (let option of options){
37
+ let parsed = option(value);
38
+ if (parsed === value) {
39
+ return parsed;
14
40
  }
41
+ }
42
+ throw new Error("...");
43
+ }, {
44
+ manualParsing: true
45
+ });
46
+ }
47
+ let commands = {
48
+ init: {
49
+ run: _init.init,
50
+ args: {
51
+ "--full": {
52
+ type: Boolean,
53
+ description: `Initialize a full \`${configs.tailwind}\` file`
54
+ },
55
+ "--postcss": {
56
+ type: Boolean,
57
+ description: `Initialize a \`${configs.postcss}\` file`
58
+ },
59
+ "-f": "--full",
60
+ "-p": "--postcss"
61
+ }
62
+ },
63
+ build: {
64
+ run: _build.build,
65
+ args: {
66
+ "--input": {
67
+ type: String,
68
+ description: "Input file"
69
+ },
70
+ "--output": {
71
+ type: String,
72
+ description: "Output file"
73
+ },
74
+ "--watch": {
75
+ type: oneOf(String, Boolean),
76
+ description: "Watch for changes and rebuild as needed"
77
+ },
78
+ "--poll": {
79
+ type: Boolean,
80
+ description: "Use polling instead of filesystem events when watching"
81
+ },
82
+ "--content": {
83
+ type: String,
84
+ description: "Content paths to use for removing unused classes"
85
+ },
86
+ "--purge": {
87
+ type: String,
88
+ deprecated: true
89
+ },
90
+ "--postcss": {
91
+ type: oneOf(String, Boolean),
92
+ description: "Load custom PostCSS configuration"
93
+ },
94
+ "--minify": {
95
+ type: Boolean,
96
+ description: "Minify the output"
97
+ },
98
+ "--config": {
99
+ type: String,
100
+ description: "Path to a custom config file"
101
+ },
102
+ "--no-autoprefixer": {
103
+ type: Boolean,
104
+ description: "Disable autoprefixer"
105
+ },
106
+ "-c": "--config",
107
+ "-i": "--input",
108
+ "-o": "--output",
109
+ "-m": "--minify",
110
+ "-w": "--watch",
111
+ "-p": "--poll"
112
+ }
113
+ }
114
+ };
115
+ let sharedFlags = {
116
+ "--help": {
117
+ type: Boolean,
118
+ description: "Display usage information"
119
+ },
120
+ "-h": "--help"
121
+ };
122
+ if (process.stdout.isTTY /* Detect redirecting output to a file */ && (process.argv[2] === undefined || process.argv.slice(2).every((flag)=>sharedFlags[flag] !== undefined))) {
123
+ (0, _help.help)({
124
+ usage: [
125
+ "tailwindcss [--input input.css] [--output output.css] [--watch] [options...]",
126
+ "tailwindcss init [--full] [--postcss] [options...]"
127
+ ],
128
+ commands: Object.keys(commands).filter((command)=>command !== "build").map((command)=>`${command} [options]`),
129
+ options: {
130
+ ...commands.build.args,
131
+ ...sharedFlags
132
+ }
133
+ });
134
+ process.exit(0);
135
+ }
136
+ let command = ((arg = "")=>arg.startsWith("-") ? undefined : arg)(process.argv[2]) || "build";
137
+ if (commands[command] === undefined) {
138
+ if (_fs.default.existsSync(_path.default.resolve(command))) {
139
+ // TODO: Deprecate this in future versions
140
+ // Check if non-existing command, might be a file.
141
+ command = "build";
142
+ } else {
143
+ (0, _help.help)({
144
+ message: `Invalid command: ${command}`,
145
+ usage: [
146
+ "tailwindcss <command> [options]"
147
+ ],
148
+ commands: Object.keys(commands).filter((command)=>command !== "build").map((command)=>`${command} [options]`),
149
+ options: sharedFlags
150
+ });
151
+ process.exit(1);
152
+ }
153
+ }
154
+ // Execute command
155
+ let { args: flags , run } = commands[command];
156
+ let args = (()=>{
157
+ try {
158
+ let result = (0, _arg.default)(Object.fromEntries(Object.entries({
159
+ ...flags,
160
+ ...sharedFlags
161
+ }).filter(([_key, value])=>{
162
+ var _value_type;
163
+ return !(value === null || value === void 0 ? void 0 : (_value_type = value.type) === null || _value_type === void 0 ? void 0 : _value_type.manualParsing);
164
+ }).map(([key, value])=>[
165
+ key,
166
+ typeof value === "object" ? value.type : value
167
+ ])), {
168
+ permissive: true
15
169
  });
170
+ // Manual parsing of flags to allow for special flags like oneOf(Boolean, String)
171
+ for(let i = result["_"].length - 1; i >= 0; --i){
172
+ let flag = result["_"][i];
173
+ if (!flag.startsWith("-")) continue;
174
+ let [flagName, flagValue] = flag.split("=");
175
+ let handler = flags[flagName];
176
+ // Resolve flagName & handler
177
+ while(typeof handler === "string"){
178
+ flagName = handler;
179
+ handler = flags[handler];
180
+ }
181
+ if (!handler) continue;
182
+ let args = [];
183
+ let offset = i + 1;
184
+ // --flag value syntax was used so we need to pull `value` from `args`
185
+ if (flagValue === undefined) {
186
+ // Parse args for current flag
187
+ while(result["_"][offset] && !result["_"][offset].startsWith("-")){
188
+ args.push(result["_"][offset++]);
189
+ }
190
+ // Cleanup manually parsed flags + args
191
+ result["_"].splice(i, 1 + args.length);
192
+ // No args were provided, use default value defined in handler
193
+ // One arg was provided, use that directly
194
+ // Multiple args were provided so pass them all in an array
195
+ flagValue = args.length === 0 ? undefined : args.length === 1 ? args[0] : args;
196
+ } else {
197
+ // Remove the whole flag from the args array
198
+ result["_"].splice(i, 1);
199
+ }
200
+ // Set the resolved value in the `result` object
201
+ result[flagName] = handler.type(flagValue, flagName);
202
+ }
203
+ // Ensure that the `command` is always the first argument in the `args`.
204
+ // This is important so that we don't have to check if a default command
205
+ // (build) was used or not from within each plugin.
206
+ //
207
+ // E.g.: tailwindcss input.css -> _: ['build', 'input.css']
208
+ // E.g.: tailwindcss build input.css -> _: ['build', 'input.css']
209
+ if (result["_"][0] !== command) {
210
+ result["_"].unshift(command);
211
+ }
212
+ return result;
213
+ } catch (err) {
214
+ if (err.code === "ARG_UNKNOWN_OPTION") {
215
+ (0, _help.help)({
216
+ message: err.message,
217
+ usage: [
218
+ "tailwindcss <command> [options]"
219
+ ],
220
+ options: sharedFlags
221
+ });
222
+ process.exit(1);
223
+ }
224
+ throw err;
225
+ }
226
+ })();
227
+ if (args["--help"]) {
228
+ (0, _help.help)({
229
+ options: {
230
+ ...flags,
231
+ ...sharedFlags
232
+ },
233
+ usage: [
234
+ `tailwindcss ${command} [options]`
235
+ ]
16
236
  });
17
- return from;
237
+ process.exit(0);
18
238
  }
239
+ run(args, configs);
@@ -16,8 +16,8 @@ function _interopRequireDefault(obj) {
16
16
  }
17
17
  function init(args, configs) {
18
18
  let messages = [];
19
- var ref;
20
- let tailwindConfigLocation = _path.default.resolve((ref = args["_"][1]) !== null && ref !== void 0 ? ref : `./${configs.tailwind}`);
19
+ var _args___;
20
+ let tailwindConfigLocation = _path.default.resolve((_args___ = args["_"][1]) !== null && _args___ !== void 0 ? _args___ : `./${configs.tailwind}`);
21
21
  if (_fs.default.existsSync(tailwindConfigLocation)) {
22
22
  messages.push(`${_path.default.basename(tailwindConfigLocation)} already exists.`);
23
23
  } else {