Skip to content

Commit 0a4de3f

Browse files
authored
Merge pull request #90 from css-blocks/add-broccoli-test
Add broccoli test
2 parents 51bb09f + 0063df3 commit 0a4de3f

12 files changed

Lines changed: 455 additions & 13 deletions

File tree

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@
3636
"remap-istanbul": "^0.9.5",
3737
"source-map-support": "^0.4.15",
3838
"test-console": "^1.1.0",
39-
"tslint": "^5.9.1",
4039
"ts-node": "^3.0.4",
40+
"tslint": "^5.9.1",
4141
"typedoc": "^0.7.1",
42-
"typescript": "^2.7.2",
42+
"typescript": "^2.8.1",
4343
"watch": "^1.0.2"
4444
},
4545
"workspaces": [
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "@css-blocks/broccoli",
3+
"version": "0.0.1",
4+
"description": "CSS Blocks Broccoli Addon",
5+
"main": "dist/src/index.js",
6+
"author": "Adam Miller <ammiller@linkedin.com>",
7+
"license": "MIT",
8+
"keywords": ["css-blocks", "css blocks", "broccoli-plugin"],
9+
"scripts": {
10+
"test": "mocha --opts test/mocha.opts dist/test",
11+
"compile": "rm -rf dist && tsc -p tsconfig.json",
12+
"pretest": "yarn run compile",
13+
"posttest": "yarn run lint",
14+
"prepublish": "yarn run compile && yarn run lintall",
15+
"lint": "tslint -t msbuild --project . -c tslint.cli.json",
16+
"lintall": "tslint -t msbuild --project . -c tslint.release.json",
17+
"lintfix": "tslint -t msbuild --project . -c tslint.cli.json --fix",
18+
"coverage":
19+
"istanbul cover -i dist/src/**/*.js --dir ./build/coverage node_modules/mocha/bin/_mocha -- dist/test --opts test/mocha.opts",
20+
"remap":
21+
"remap-istanbul -i build/coverage/coverage.json -o coverage -t html",
22+
"docs": "typedoc --out ./docs .",
23+
"watch": "watch 'yarn run test' src test types-local --wait=1"
24+
},
25+
"devDependencies": {
26+
"@css-blocks/code-style": "^0.17.0",
27+
"@css-blocks/glimmer-templates": "^0.17.0"
28+
},
29+
"dependencies": {
30+
"@glimmer/compiler": "^0.33.0",
31+
"@glimmer/syntax": "^0.33.0",
32+
"@types/recursive-readdir": "^2.2.0",
33+
"broccoli-funnel": "^2.0.1",
34+
"broccoli-merge-trees": "^3.0.0",
35+
"broccoli-plugin": "^1.3.0",
36+
"broccoli-test-helper": "^1.2.0",
37+
"colors": "^1.2.1",
38+
"css-blocks": "^0.17.0",
39+
"debug": "^3.1.0",
40+
"fs-extra": "^5.0.0",
41+
"opticss": "file:../../../opticss/packages/opticss",
42+
"postcss": "^6.0.21",
43+
"recursive-readdir": "^2.2.2",
44+
"walk-sync": "^0.3.2"
45+
}
46+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import * as fs from "fs-extra";
2+
import * as path from "path";
3+
4+
import { Analyzer, BlockCompiler, StyleMapping } from "css-blocks";
5+
import { Optimizer } from "opticss";
6+
import * as postcss from "postcss";
7+
import * as readdir from "recursive-readdir";
8+
9+
import { TemplateTypes } from "@opticss/template-api";
10+
11+
import { BroccoliPlugin } from "./utils";
12+
13+
export interface BroccoliOptions {
14+
entry: string[];
15+
output: string;
16+
analyzer: Analyzer<keyof TemplateTypes>;
17+
transport: {[key: string]: object};
18+
}
19+
20+
class BroccoliCSSBlocks extends BroccoliPlugin {
21+
22+
private analyzer: Analyzer<keyof TemplateTypes>;
23+
private entry: string[];
24+
private output: string;
25+
private transport: { [key: string]: object };
26+
private optimizationOptions: object = {};
27+
28+
// tslint:disable-next-line:prefer-whatever-to-any
29+
constructor(inputNode: any, options: BroccoliOptions) {
30+
super([inputNode], { name: "broccoli-css-blocks" });
31+
32+
this.entry = options.entry;
33+
this.output = options.output;
34+
this.analyzer = options.analyzer;
35+
this.transport = options.transport;
36+
37+
if (!this.output) {
38+
throw new Error("CSS Blocks Broccoli Plugin requires an output file name.");
39+
}
40+
}
41+
42+
async build() {
43+
let options = this.analyzer.cssBlocksOptions;
44+
let blockCompiler = new BlockCompiler(postcss, options);
45+
let optimizer = new Optimizer(this.optimizationOptions, this.analyzer.optimizationOptions);
46+
47+
// This build step is *mostly* just a pass-through of all files!
48+
// QUESTION: Tom, is there a better way to do this in Broccoli?
49+
let files = await readdir(this.inputPaths[0]);
50+
for (let file of files) {
51+
file = path.relative(this.inputPaths[0], file);
52+
await fs.ensureDir(path.join(this.outputPath, path.dirname(file)));
53+
try {
54+
await fs.symlink(
55+
path.join(this.inputPaths[0], file),
56+
path.join(this.outputPath, file),
57+
);
58+
} catch (e) {
59+
console.log("Error linking", path.join(this.inputPaths[0], file), "to output directory.");
60+
}
61+
}
62+
63+
// Oh hey look, we're analyzing.
64+
await this.analyzer.analyze(...this.entry);
65+
66+
// Compile all Blocks and add them as sources to the Optimizer.
67+
// TODO: handle a sourcemap from compiling the block file via a preprocessor.
68+
let blocks = this.analyzer.transitiveBlockDependencies();
69+
for (let block of blocks) {
70+
if (block.stylesheet) {
71+
let root = blockCompiler.compile(block, block.stylesheet, this.analyzer);
72+
let result = root.toResult({ to: this.output, map: { inline: false, annotation: false } });
73+
let filesystemPath = options.importer.filesystemPath(block.identifier, options);
74+
let filename = filesystemPath || options.importer.debugIdentifier(block.identifier, options);
75+
76+
// If this Block has a representation on disk, remove it from our output tree.
77+
// TODO: This isn't working right now because `importer.filesystemPath` doesn't return the expected path...
78+
if (filesystemPath) {
79+
await fs.remove(path.join(this.outputPath, path.relative(options.rootDir, filesystemPath)));
80+
}
81+
82+
// Add the compiled Block file to the optimizer.
83+
optimizer.addSource({
84+
content: result.css,
85+
filename,
86+
sourceMap: result.map.toJSON(),
87+
});
88+
}
89+
}
90+
91+
// Add each Analysis to the Optimizer.
92+
this.analyzer.eachAnalysis((a) => optimizer.addAnalysis(a.forOptimizer(options)));
93+
94+
// Run optimization and compute StyleMapping.
95+
let optimized = await optimizer.optimize(this.output);
96+
let styleMapping = new StyleMapping(optimized.styleMapping, blocks, options, this.analyzer.analyses());
97+
98+
// Attach all computed data to our magic shared memory transport object...
99+
this.transport.mapping = styleMapping;
100+
this.transport.blocks = blocks;
101+
this.transport.analyzer = this.analyzer;
102+
this.transport.css = optimized.output;
103+
104+
// Write our compiled CSS to the output tree.
105+
// QUESTION: GUH! TOM! THIS DOESN'T APPEAR IN THE OUTPUT TREE!
106+
await fs.outputFile(
107+
path.join(this.outputPath, this.output),
108+
optimized.output.content.toString(),
109+
);
110+
111+
}
112+
113+
}
114+
115+
export { BroccoliCSSBlocks };
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* This file takes heavy inspiration from other Broccoli Plugins written in
3+
* Typescript to provide sane typings for common Broccoli utilities.
4+
*/
5+
/* tslint:disable */
6+
export const BroccoliPlugin: BroccoliPlugin.Static = require("broccoli-plugin");
7+
export const walkSync: WalkSync = require("walk-sync");
8+
9+
declare function require(id: string): any;
10+
11+
export namespace BroccoliPlugin {
12+
export interface PluginOptions {
13+
name?: string;
14+
annotation?: string;
15+
persistentOutput?: boolean;
16+
}
17+
18+
export interface Plugin {
19+
inputPaths: string[];
20+
outputPath: string;
21+
cachePath: string;
22+
}
23+
24+
export interface Static {
25+
new(inputNodes: any[], options?: any): Plugin;
26+
}
27+
}
28+
29+
export interface WalkSync {
30+
(path: string, options?: any): string[];
31+
entries(path: string, options?: any): WalkSync.Entry[];
32+
}
33+
34+
export namespace WalkSync {
35+
export type Row = string | RegExp[];
36+
37+
export interface Entry {
38+
relativePath: string;
39+
basePath: string;
40+
fullPath: string;
41+
checksum: string;
42+
mode: number;
43+
size: number;
44+
mtime: Date;
45+
isDirectory(): boolean;
46+
}
47+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { GlimmerAnalyzer } from "@css-blocks/glimmer-templates";
2+
import * as assert from "assert";
3+
import { TempDir, buildOutput, createTempDir } from "broccoli-test-helper";
4+
5+
import { BroccoliCSSBlocks } from "../src/index";
6+
7+
describe("Broccoli Plugin Test", function() {
8+
let input: TempDir;
9+
10+
beforeEach(async () => {
11+
input = await createTempDir();
12+
});
13+
14+
afterEach(async () => {
15+
await input.dispose();
16+
});
17+
18+
describe("Broccoli Plugin Test", () => {
19+
it("runs tests", () => {
20+
assert.ok(1);
21+
});
22+
23+
it("outputs CSS file and populates transport object", async () => {
24+
const entryComponentName = "Chrisrng";
25+
26+
input.write({
27+
"package.json": `{
28+
"name": "chrisrng-test"
29+
}`,
30+
src: {
31+
ui: {
32+
components: {
33+
[entryComponentName]: {
34+
"template.hbs": `<div><h1 class="foo">Welcome to Glimmer!</h1></div>`,
35+
"stylesheet.block.css": `:scope {
36+
color: red;
37+
}
38+
39+
.foo {
40+
color: green;
41+
}`,
42+
},
43+
},
44+
},
45+
},
46+
});
47+
48+
let analyzer = new GlimmerAnalyzer(input.path());
49+
let transport = {};
50+
51+
let compiler = new BroccoliCSSBlocks(input.path(), {
52+
entry: [entryComponentName],
53+
output: "src/ui/styles/css-blocks.css",
54+
transport,
55+
analyzer,
56+
});
57+
58+
let output = await buildOutput(compiler);
59+
let files = output.read();
60+
61+
assert.ok(Object.keys(transport).length, "Transport Object populated");
62+
assert.ok(transport["mapping"], "Mapping property is populated in Transport Object");
63+
assert.ok(transport["blocks"], "Blocks property is populated in Transport Object");
64+
assert.ok(transport["analyzer"], "Analyzer property is populated in Transport Object");
65+
assert.ok(transport["css"], "CSS property is populated in Transport Object");
66+
67+
assert.ok(files["src"]!["ui"]["styles"]["css-blocks.css"], "CSS File generated");
68+
});
69+
});
70+
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"include": [
4+
"src"
5+
]
6+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"extends": "../../tsconfig.json",
3+
"compilerOptions": {
4+
"outDir": "dist",
5+
"baseUrl": "dist"
6+
},
7+
"include": [
8+
"src",
9+
"test"
10+
],
11+
"exclude": [
12+
"sanity",
13+
"dist",
14+
"node_modules"
15+
]
16+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"$schema": "http://json.schemastore.org/tslint",
3+
"extends": "@css-blocks/code-style/configs/tslint.cli.json"
4+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "@css-blocks/code-style/configs/tslint.cli.json"
3+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"$schema": "http://json.schemastore.org/tslint",
3+
"extends": "@css-blocks/code-style/configs/tslint.release.json"
4+
}

0 commit comments

Comments
 (0)