Skip to content

Commit 85be93b

Browse files
committed
feat: Centralize ember config and use it in ember & ember-app.
1 parent ea27173 commit 85be93b

File tree

7 files changed

+113
-90
lines changed

7 files changed

+113
-90
lines changed

packages/@css-blocks/ember-app/src/brocolli-plugin.ts

+40-20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Block, BlockCompiler, BlockFactory, Options as CSSBlocksOptions, SerializedSourceAnalysis, resolveConfiguration } from "@css-blocks/core";
2-
import { BroccoliTreeImporter, EmberAnalysis, EmberAnalyzer, TEMPLATE_TYPE, pathToIdent } from "@css-blocks/ember-support";
1+
import { Block, BlockCompiler, BlockFactory, SerializedSourceAnalysis, resolveConfiguration } from "@css-blocks/core";
2+
import { BroccoliTreeImporter, EmberAnalysis, EmberAnalyzer, ResolvedCSSBlocksEmberOptions, TEMPLATE_TYPE, pathToIdent } from "@css-blocks/ember-support";
33
import { unionInto } from "@opticss/util";
44
import mergeTrees = require("broccoli-merge-trees");
55
import type { InputNode } from "broccoli-node-api";
@@ -8,7 +8,7 @@ import Plugin = require("broccoli-plugin");
88
import type { PluginOptions } from "broccoli-plugin/dist/interfaces";
99
import debugGenerator from "debug";
1010
import * as FSTree from "fs-tree-diff";
11-
import { Optimizer, postcss } from "opticss";
11+
import { OptiCSSOptions, Optimizer, postcss } from "opticss";
1212
import * as path from "path";
1313

1414
import { RuntimeDataGenerator } from "./RuntimeDataGenerator";
@@ -18,8 +18,8 @@ const debug = debugGenerator("css-blocks:ember-app");
1818
export class CSSBlocksApplicationPlugin extends Filter {
1919
appName: string;
2020
previousSourceTree: FSTree;
21-
cssBlocksOptions: CSSBlocksOptions;
22-
constructor(appName: string, inputNodes: InputNode[], cssBlocksOptions: CSSBlocksOptions, options?: PluginOptions) {
21+
cssBlocksOptions: ResolvedCSSBlocksEmberOptions;
22+
constructor(appName: string, inputNodes: InputNode[], cssBlocksOptions: ResolvedCSSBlocksEmberOptions, options?: PluginOptions) {
2323
super(mergeTrees(inputNodes), options || {});
2424
this.appName = appName;
2525
this.previousSourceTree = new FSTree();
@@ -39,24 +39,13 @@ export class CSSBlocksApplicationPlugin extends Filter {
3939
} else {
4040
this.previousSourceTree = currentFSTree;
4141
}
42-
let config = resolveConfiguration(this.cssBlocksOptions);
42+
let config = resolveConfiguration(this.cssBlocksOptions.parserOpts);
4343
let importer = new BroccoliTreeImporter(this.input, null, config.importer);
4444
config = resolveConfiguration({importer}, config);
4545
let factory = new BlockFactory(config, postcss);
46-
let analyzer = new EmberAnalyzer(factory);
47-
// TODO: Make this configurable from the ember app.
48-
let optimizerOptions = {
49-
enabled: true,
50-
rewriteIdents: {
51-
id: false,
52-
class: true,
53-
omitIdents: {
54-
class: [], // TODO: scan css files for other classes in use.
55-
},
56-
},
57-
removeUnusedStyles: true,
58-
mergeDeclarations: true,
59-
};
46+
let analyzer = new EmberAnalyzer(factory, this.cssBlocksOptions.analysisOpts);
47+
let optimizerOptions = this.cssBlocksOptions.optimization;
48+
this.reserveClassnames(optimizerOptions);
6049
let optimizer = new Optimizer(optimizerOptions, analyzer.optimizationOptions);
6150
let blocksUsed = new Set<Block>();
6251
for (let entry of entries) {
@@ -114,6 +103,37 @@ export class CSSBlocksApplicationPlugin extends Filter {
114103
export const data = ${serializedData};
115104
`);
116105
}
106+
107+
/**
108+
* Modifies the options passed in to supply the CSS classnames used in the
109+
* application to the the list of identifiers that should be omitted by the
110+
* classname generator.
111+
*/
112+
reserveClassnames(optimizerOptions: Partial<OptiCSSOptions>): void {
113+
let rewriteIdents = optimizerOptions.rewriteIdents;
114+
let rewriteIdentsFlag: boolean;
115+
let omitIdents: Array<string>;
116+
if (typeof rewriteIdents === "boolean") {
117+
rewriteIdentsFlag = rewriteIdents;
118+
omitIdents = [];
119+
} else if (typeof rewriteIdents === "undefined") {
120+
rewriteIdentsFlag = true;
121+
omitIdents = [];
122+
} else {
123+
rewriteIdentsFlag = rewriteIdents.class;
124+
omitIdents = rewriteIdents.omitIdents && rewriteIdents.omitIdents.class || [];
125+
}
126+
127+
// TODO: scan css files for other classes in use and add them to `omitIdents`.
128+
129+
optimizerOptions.rewriteIdents = {
130+
id: false,
131+
class: rewriteIdentsFlag,
132+
omitIdents: {
133+
class: omitIdents,
134+
},
135+
};
136+
}
117137
}
118138

119139
/**

packages/@css-blocks/ember-app/src/index.ts

+11-26
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { CSSBlocksEmberOptions, ResolvedCSSBlocksEmberOptions, getConfig } from "@css-blocks/ember-support";
12
import BroccoliDebug = require("broccoli-debug");
23
import funnel = require("broccoli-funnel");
34
import mergeTrees = require("broccoli-merge-trees");
@@ -14,6 +15,7 @@ interface AddonEnvironment {
1415
rootDir: string;
1516
isApp: boolean;
1617
modulePrefix: string;
18+
config: ResolvedCSSBlocksEmberOptions;
1719
}
1820

1921
interface CSSBlocksApplicationAddon {
@@ -107,12 +109,20 @@ const EMBER_ADDON: AddonImplementation<CSSBlocksApplicationAddon> = {
107109

108110
let modulePrefix = this._modulePrefix();
109111

112+
let appOptions = app!.options;
113+
if (!appOptions["css-blocks"]) {
114+
appOptions["css-blocks"] = {};
115+
}
116+
// Get CSS Blocks options provided by the application, if present.
117+
let config = getConfig(rootDir, app!.isProduction, <CSSBlocksEmberOptions>appOptions["css-blocks"]);
118+
110119
return {
111120
parent,
112121
app: app!,
113122
rootDir,
114123
isApp,
115124
modulePrefix,
125+
config,
116126
};
117127
},
118128

@@ -134,7 +144,6 @@ const EMBER_ADDON: AddonImplementation<CSSBlocksApplicationAddon> = {
134144
* @returns - A tree that's ready to process.
135145
*/
136146
preprocessTree(type, tree) {
137-
// tslint:disable-next-line:prefer-unknown-to-any
138147
let env = this.env!;
139148

140149
if (type === "js") {
@@ -153,7 +162,7 @@ const EMBER_ADDON: AddonImplementation<CSSBlocksApplicationAddon> = {
153162
return findCssBlocksTemplateOutputTree(publicTree.inputNodes);
154163
}).filter(Boolean);
155164
let lazyOutput = funnel(mergeTrees(jsOutputTrees), {destDir: "lazy-tree-output"});
156-
this.broccoliAppPluginInstance = new CSSBlocksApplicationPlugin(env.modulePrefix, [env.app.addonTree(), tree, lazyOutput], {});
165+
this.broccoliAppPluginInstance = new CSSBlocksApplicationPlugin(env.modulePrefix, [env.app.addonTree(), tree, lazyOutput], env.config);
157166
let debugTree = new BroccoliDebug(this.broccoliAppPluginInstance, `css-blocks:optimized`);
158167
return funnel(debugTree, {srcDir: env.modulePrefix, destDir: env.modulePrefix});
159168
} else {
@@ -176,30 +185,6 @@ const EMBER_ADDON: AddonImplementation<CSSBlocksApplicationAddon> = {
176185
return tree;
177186
}
178187
},
179-
180-
/**
181-
* Post-process a tree. Runs after the files in this tree have been built.
182-
* @param type - What kind of tree.
183-
* @param tree - The processed tree.
184-
* @returns - The processed tree.
185-
*/
186-
postprocessTree(type, tree) {
187-
if (type !== "template") return tree;
188-
189-
// TODO: Do something in the template tree.
190-
return tree;
191-
},
192-
193-
/**
194-
* Used to add preprocessors to the preprocessor registry. This is often used
195-
* by addons like ember-cli-htmlbars and ember-cli-coffeescript to add a
196-
* template or js preprocessor to the registry.
197-
* @param _type - Either "self" or "parent".
198-
* @param _registry - The registry to be set up.
199-
*/
200-
setupPreprocessorRegistry(_type, _registry) {
201-
// TODO: This hook may not be necessary in this addon.
202-
},
203188
};
204189

205190
type MaybeCSSBlocksTree = MaybeCSSBlocksTreePlugin | string;

packages/@css-blocks/ember-support/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,18 @@
2929
},
3030
"devDependencies": {
3131
"@css-blocks/code-style": "^1.0.0",
32+
"@opticss/util": "^0.7.0",
3233
"@types/chai-as-promised": "^7.1.2",
3334
"chai-as-promised": "^7.1.1",
35+
"opticss": "^0.7.0",
3436
"typescript": "~3.8.3",
3537
"watch": "^1.0.2"
3638
},
3739
"peerDependencies": {
3840
"ember-cli-htmlbars": "^4.3.1"
3941
},
4042
"dependencies": {
43+
"@css-blocks/config": "^1.0.0",
4144
"@css-blocks/core": "^1.0.0",
4245
"@opticss/template-api": "^0.6.3",
4346
"fs-merger": "^3.0.2"
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from "./BroccoliTreeImporter";
2+
export * from "./options";
23
export * from "./EmberAnalysis";
34
export * from "./HandlebarsTemplate";
45
export * from "./EmberAnalyzer";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import * as config from "@css-blocks/config";
2+
import { AnalysisOptions, NodeJsImporter, Options as ParserOptions, OutputMode } from "@css-blocks/core";
3+
import type { ObjectDictionary } from "@opticss/util";
4+
import type { OptiCSSOptions } from "opticss";
5+
6+
type Writeable<T> = { -readonly [P in keyof T]: T[P] };
7+
8+
export interface CSSBlocksEmberOptions {
9+
output?: string;
10+
aliases?: ObjectDictionary<string>;
11+
analysisOpts?: AnalysisOptions;
12+
parserOpts?: Writeable<ParserOptions>;
13+
optimization?: Partial<OptiCSSOptions>;
14+
}
15+
16+
export interface ResolvedCSSBlocksEmberOptions {
17+
output?: string;
18+
aliases: ObjectDictionary<string>;
19+
analysisOpts: AnalysisOptions;
20+
parserOpts: ParserOptions;
21+
optimization: Partial<OptiCSSOptions>;
22+
}
23+
24+
export function getConfig(root: string, isProduction: boolean, options: CSSBlocksEmberOptions): ResolvedCSSBlocksEmberOptions {
25+
if (!options.aliases) options.aliases = {};
26+
if (!options.analysisOpts) options.analysisOpts = {};
27+
if (!options.optimization) options.optimization = {};
28+
29+
if (!options.parserOpts) {
30+
options.parserOpts = config.searchSync(root) || {};
31+
}
32+
33+
// Use the node importer by default.
34+
options.parserOpts.importer = options.parserOpts.importer || new NodeJsImporter(options.aliases);
35+
36+
if (typeof options.optimization.enabled === "undefined") {
37+
options.optimization.enabled = isProduction;
38+
}
39+
40+
// Update parserOpts to include the absolute path to our application code directory.
41+
if (!options.parserOpts.rootDir) {
42+
options.parserOpts.rootDir = root;
43+
}
44+
options.parserOpts.outputMode = OutputMode.BEM_UNIQUE;
45+
46+
if (options.output !== undefined && typeof options.output !== "string") {
47+
throw new Error(`Invalid css-blocks options in 'ember-cli-build.js': Output must be a string. Instead received ${options.output}.`);
48+
}
49+
return <ResolvedCSSBlocksEmberOptions>options;
50+
}

packages/@css-blocks/ember/src/CSSBlocksTemplateCompilerPlugin.ts

+4-14
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { AnalysisOptions, Block, BlockCompiler, BlockDefinitionCompiler, BlockFactory, Configuration, INLINE_DEFINITION_FILE, Options as ParserOptions, resolveConfiguration } from "@css-blocks/core";
2-
import { BroccoliTreeImporter, EmberAnalysis, identToPath, isBroccoliTreeIdentifier } from "@css-blocks/ember-support";
1+
import { Block, BlockCompiler, BlockDefinitionCompiler, BlockFactory, Configuration, INLINE_DEFINITION_FILE, resolveConfiguration } from "@css-blocks/core";
2+
import { BroccoliTreeImporter, CSSBlocksEmberOptions, EmberAnalysis, identToPath, isBroccoliTreeIdentifier } from "@css-blocks/ember-support";
33
import type { ASTPluginEnvironment } from "@glimmer/syntax";
4-
import { MultiMap, ObjectDictionary } from "@opticss/util";
4+
import { MultiMap } from "@opticss/util";
55
import type { InputNode } from "broccoli-node-api";
66
import outputWrapper = require("broccoli-output-wrapper");
77
import md5Sum = require("broccoli-persistent-filter/lib/md5-hex");
@@ -11,7 +11,7 @@ import TemplateCompilerPlugin = require("ember-cli-htmlbars/lib/template-compile
1111
import FSMerger = require("fs-merger");
1212
import type { FS as MergedFileSystem } from "fs-merger";
1313
import * as FSTree from "fs-tree-diff";
14-
import { OptiCSSOptions, postcss } from "opticss";
14+
import { postcss } from "opticss";
1515
import * as path from "path";
1616

1717
import { AnalyzingRewriteManager } from "./AnalyzingRewriteManager";
@@ -26,8 +26,6 @@ interface AdditionalFile {
2626
}
2727
export const BLOCK_GLOB = "**/*.block.{css,scss,sass,less,styl}";
2828

29-
type Writeable<T> = { -readonly [P in keyof T]: T[P] };
30-
3129
const debug = debugGenerator("css-blocks:ember");
3230

3331
export interface EmberASTPluginEnvironment extends ASTPluginEnvironment {
@@ -36,14 +34,6 @@ export interface EmberASTPluginEnvironment extends ASTPluginEnvironment {
3634
};
3735
}
3836

39-
export interface CSSBlocksEmberOptions {
40-
output?: string;
41-
aliases?: ObjectDictionary<string>;
42-
analysisOpts?: AnalysisOptions;
43-
parserOpts?: Writeable<ParserOptions>;
44-
optimization?: Partial<OptiCSSOptions>;
45-
}
46-
4737
/**
4838
* This class extends ember-cli-htmlbars's template compiler (which is built on
4939
* top of broccoli-persistent-filter). In the ember-cli addon for this package

packages/@css-blocks/ember/src/index.ts

+4-30
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import * as config from "@css-blocks/config";
2-
import { NodeJsImporter, Options as ParserOptions, OutputMode } from "@css-blocks/core";
1+
import { Options as ParserOptions } from "@css-blocks/core";
2+
import { CSSBlocksEmberOptions, getConfig } from "@css-blocks/ember-support";
33
import type { ASTPlugin } from "@glimmer/syntax";
44
import { ObjectDictionary } from "@opticss/util";
55
import BroccoliDebug = require("broccoli-debug");
@@ -12,7 +12,7 @@ import type EmberAddon from "ember-cli/lib/models/addon";
1212
import type { AddonImplementation, ThisAddon, Tree } from "ember-cli/lib/models/addon";
1313
import type Project from "ember-cli/lib/models/project";
1414

15-
import { BLOCK_GLOB, CSSBlocksEmberOptions, CSSBlocksTemplateCompilerPlugin, EmberASTPluginEnvironment } from "./CSSBlocksTemplateCompilerPlugin";
15+
import { BLOCK_GLOB, CSSBlocksTemplateCompilerPlugin, EmberASTPluginEnvironment } from "./CSSBlocksTemplateCompilerPlugin";
1616

1717
const debug = debugGenerator("css-blocks:ember");
1818

@@ -128,33 +128,7 @@ const EMBER_ADDON: AddonImplementation<CSSBlocksAddon> = {
128128
}
129129

130130
// Get CSS Blocks options provided by the application, if present.
131-
const options = <CSSBlocksEmberOptions>appOptions["css-blocks"]; // Do not clone! Contains non-json-safe data.
132-
if (!options.aliases) options.aliases = {};
133-
if (!options.analysisOpts) options.analysisOpts = {};
134-
if (!options.optimization) options.optimization = {};
135-
136-
if (!options.parserOpts) {
137-
options.parserOpts = config.searchSync(root) || {};
138-
}
139-
140-
// Use the node importer by default.
141-
options.parserOpts.importer = options.parserOpts.importer || new NodeJsImporter(options.aliases);
142-
143-
// Optimization is always disabled for now, until we get project-wide analysis working.
144-
if (typeof options.optimization.enabled === "undefined") {
145-
options.optimization.enabled = app.isProduction;
146-
}
147-
148-
// Update parserOpts to include the absolute path to our application code directory.
149-
if (!options.parserOpts.rootDir) {
150-
options.parserOpts.rootDir = root;
151-
}
152-
options.parserOpts.outputMode = OutputMode.BEM_UNIQUE;
153-
154-
if (options.output !== undefined && typeof options.output !== "string") {
155-
throw new Error(`Invalid css-blocks options in 'ember-cli-build.js': Output must be a string. Instead received ${options.output}.`);
156-
}
157-
return options;
131+
return getConfig(root, app.isProduction, <CSSBlocksEmberOptions>appOptions["css-blocks"]);
158132
},
159133

160134
optionsForCacheInvalidation() {

0 commit comments

Comments
 (0)