Skip to content

Commit b01fc3d

Browse files
committed
feat: Better examples. Work-in-progress.
1 parent 04f1f62 commit b01fc3d

File tree

21 files changed

+3922
-40
lines changed

21 files changed

+3922
-40
lines changed

packages/@css-blocks/jsx/.vscode/launch.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"request": "launch",
1010
"name": "Launch Program",
1111
"preLaunchTask": "compile",
12-
"program": "${workspaceRoot}/node_modules/.bin/_mocha",
12+
"program": "${workspaceRoot}/../../../node_modules/.bin/_mocha",
1313
"args": [
1414
"dist/test",
1515
"--recursive",

packages/@css-blocks/jsx/src/Analyzer/index.ts

+45-21
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,64 @@
1-
import {
2-
Analysis,
3-
Analyzer,
4-
Block,
5-
} from "@css-blocks/core";
1+
import { Analysis, Analyzer, Block } from "@css-blocks/core";
62
import { TemplateIntegrationOptions } from "@opticss/template-api";
73
import { some, unwrap } from "@opticss/util";
84
import traverse from "babel-traverse";
95
import * as babylon from "babylon";
106
import * as debugGenerator from "debug";
117
import * as fs from "fs-extra";
128
import * as path from "path";
9+
import { deprecate } from "util";
1310

14-
import { CssBlocksJSXOptions } from "../options";
11+
import { JSXOptions, JSXOptionsReader } from "../options";
1512
import { JSXParseError } from "../utils/Errors";
1613

1714
import { JSXTemplate, TEMPLATE_TYPE } from "./Template";
1815
import { elementVisitor, importVisitor } from "./visitors";
1916

20-
const debug = debugGenerator("css-blocks:jsx:Analyzer");
21-
2217
export type JSXAnalysis = Analysis<TEMPLATE_TYPE>;
2318

19+
export interface AnalyzerOptions {
20+
/** A name that is used (if provided) in logging to distinguish output of different analyzers in a build. */
21+
analyzerName?: string;
22+
}
23+
24+
const deprecatedName = deprecate(
25+
(name: string, options: JSXOptions & AnalyzerOptions) => {
26+
options.analyzerName = name;
27+
},
28+
"The name parameter of the JSX Analyzer is deprecated and usually unnecessary.\n" +
29+
"Pass only options and set the analyzerName option there if you really need it (you really don't).",
30+
);
31+
2432
export class CSSBlocksJSXAnalyzer extends Analyzer<TEMPLATE_TYPE> {
25-
private options: CssBlocksJSXOptions;
33+
private options: JSXOptionsReader;
2634

27-
public name: string;
35+
public name?: string;
2836
public analysisPromises: Map<string, Promise<JSXAnalysis>>;
2937
public blockPromises: Map<string, Promise<Block>>;
38+
private debug: debugGenerator.IDebugger;
3039

31-
constructor(name: string, options: Partial<CssBlocksJSXOptions> = {}) {
32-
let opts = new CssBlocksJSXOptions(options);
33-
super(opts.compilationOptions);
34-
this.name = name;
35-
this.options = opts;
40+
constructor(options: JSXOptions & AnalyzerOptions);
41+
/**
42+
* @deprecated Use the single argument constructor.
43+
* @param name Deprecated. Pass the analyzerName option instead;
44+
*/
45+
constructor(name: string | JSXOptions & AnalyzerOptions, options: JSXOptions & AnalyzerOptions = {}) {
46+
// ewww need to get rid of this deprecation soon.
47+
super(options && options.compilationOptions || (name && typeof name !== "string" && name.compilationOptions) || {});
48+
if (typeof name === "string") {
49+
deprecatedName(name, options);
50+
} else {
51+
options = name;
52+
}
53+
this.name = options.analyzerName;
54+
this.options = new JSXOptionsReader(options);
3655
this.analysisPromises = new Map();
3756
this.blockPromises = new Map();
57+
let debugIdent = "css-blocks:jsx:Analyzer";
58+
if (this.name) {
59+
debugIdent += `:${this.name}`;
60+
}
61+
this.debug = debugGenerator(debugIdent);
3862
}
3963

4064
public reset() {
@@ -67,7 +91,7 @@ export class CSSBlocksJSXAnalyzer extends Analyzer<TEMPLATE_TYPE> {
6791
promises.push(this.parseFile(path.join(dir, entryPoint)));
6892
}
6993
await Promise.all(promises);
70-
debug(`Found ${this.analysisPromises.size} analysis promises`);
94+
this.debug(`Found ${this.analysisPromises.size} analysis promises`);
7195
return this;
7296
}
7397

@@ -105,11 +129,11 @@ export class CSSBlocksJSXAnalyzer extends Analyzer<TEMPLATE_TYPE> {
105129
process.chdir(oldDir);
106130

107131
// Wait for all block promises to resolve then resolve with the finished analysis.
108-
debug(`Waiting for ${blockPromises.length} Block imported by "${template.identifier}" to finish compilation.`);
132+
this.debug(`Waiting for ${blockPromises.length} Block imported by "${template.identifier}" to finish compilation.`);
109133
await Promise.all(blockPromises);
110-
debug(`Waiting for ${childTemplatePromises.length} child templates to finish analysis before analysis of ${template.identifier}.`);
134+
this.debug(`Waiting for ${childTemplatePromises.length} child templates to finish analysis before analysis of ${template.identifier}.`);
111135
await Promise.all(childTemplatePromises);
112-
debug(`All child compilations finished for "${template.identifier}".`);
136+
this.debug(`All child compilations finished for "${template.identifier}".`);
113137
return analysis;
114138

115139
}
@@ -123,11 +147,11 @@ export class CSSBlocksJSXAnalyzer extends Analyzer<TEMPLATE_TYPE> {
123147

124148
let template: JSXTemplate = new JSXTemplate(filename, data);
125149

126-
debug(`Beginning imports crawl of ${filename}.`);
150+
this.debug(`Beginning imports crawl of ${filename}.`);
127151
let analysisPromise = this.crawl(template);
128152
this.analysisPromises.set(template.identifier, analysisPromise);
129153
let analysis = await analysisPromise;
130-
debug(`Finished imports crawl of ${filename}.`);
154+
this.debug(`Finished imports crawl of ${filename}.`);
131155

132156
traverse(unwrap(analysis.template.ast), elementVisitor(analysis));
133157
// No need to keep detailed template data anymore!

packages/@css-blocks/jsx/src/Analyzer/visitors/importer.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
import * as debugGenerator from "debug";
1919

2020
import { JSXTemplate, TEMPLATE_TYPE } from "../../Analyzer/Template";
21-
import { CssBlocksJSXOptions } from "../../options";
21+
import { JSXOptionsReader } from "../../options";
2222
import { ErrorLocation, TemplateImportError } from "../../utils/Errors";
2323
import { isBlockFilename } from "../../utils/isBlockFilename";
2424
import { CSSBlocksJSXAnalyzer } from "../index";
@@ -58,7 +58,7 @@ export function importVisitor(
5858
analysis: Analysis<TEMPLATE_TYPE>,
5959
blockPromises: Promise<Block>[],
6060
childTemplatePromises: Promise<Analysis<TEMPLATE_TYPE>>[],
61-
options: CssBlocksJSXOptions,
61+
options: JSXOptionsReader,
6262
) {
6363

6464
// Keep a running record of local block names while traversing so we can check

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export { CSSBlocksJSXAnalyzer as Analyzer } from "./Analyzer";
1+
export { CSSBlocksJSXAnalyzer as Analyzer, AnalyzerOptions } from "./Analyzer";
22
export { CSSBlocksJSXTransformer as Rewriter } from "./transformer";
3-
export { CssBlocksJSXOptions as Options } from "./options";
3+
export { JSXOptions as Options, JSXOptionsReader as OptionReader } from "./options";

packages/@css-blocks/jsx/src/options.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { ResolvedConfiguration, resolveConfiguration } from "@css-blocks/core";
1+
import { Configuration as CSSBlocksConfiguration, ResolvedConfiguration, resolveConfiguration } from "@css-blocks/core";
22
import { ObjectDictionary } from "@opticss/util";
3-
43
import { BabylonOptions, PluginName } from "babylon";
54

65
// We are only using the output Babylon AST to collect analysis data here,
@@ -32,14 +31,22 @@ const BABEL_PLUGINS: any = [
3231
"nullishCoalescingOperator",
3332
];
3433

35-
export class CssBlocksJSXOptions {
34+
export interface JSXOptions {
35+
baseDir?: string;
36+
types?: "typescript" | "flow" | "none";
37+
aliases?: ObjectDictionary<string>;
38+
compilationOptions?: CSSBlocksConfiguration;
39+
// resolver?: (importPath: string, fromFile?: string) => string;
40+
}
41+
42+
export class JSXOptionsReader {
3643
public baseDir: string;
37-
public types: "typescript" | "flow" | "none";
44+
public types: JSXOptions["types"];
3845
public aliases: ObjectDictionary<string>;
3946
public compilationOptions: ResolvedConfiguration;
4047
public parserOptions: BabylonOptions;
4148

42-
constructor(opts: Partial<CssBlocksJSXOptions>) {
49+
constructor(opts: JSXOptions = {}) {
4350
this.baseDir = opts.baseDir || ".";
4451
this.types = opts.types || "none";
4552
this.aliases = opts.aliases || {};

packages/@css-blocks/jsx/src/transformer/babel.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
} from "babel-types";
2727
// import { TemplateAnalysisError } from '../utils/Errors';
2828
import * as debugGenerator from "debug";
29+
import { deprecate } from "util";
2930

3031
import { TEMPLATE_TYPE } from "../Analyzer/Template";
3132
import { JSXElementAnalyzer } from "../Analyzer/visitors/element";
@@ -62,8 +63,7 @@ interface BabelFile {
6263
filename: string;
6364
};
6465
}
65-
66-
export function makePlugin(transformOpts: { rewriter: Rewriter }): () => PluginObj<CssBlocksVisitor> {
66+
export function babelPlugin(transformOpts: { rewriter: Rewriter }): () => PluginObj<CssBlocksVisitor> {
6767
const rewriter = transformOpts.rewriter;
6868
debug(`Made Rewriter`);
6969

@@ -199,6 +199,11 @@ export function makePlugin(transformOpts: { rewriter: Rewriter }): () => PluginO
199199
};
200200
}
201201

202+
/**
203+
* @deprecated call babelPlugin instead.
204+
*/
205+
export const makePlugin = deprecate(babelPlugin, "makePlugin has been renamed babelPlugin");
206+
202207
function detectStrayReferenceToImport(
203208
importDeclPath: NodePath<ImportDeclaration>,
204209
filename: string,

packages/@css-blocks/jsx/test/transformer/transformer-test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import * as testConsole from "test-console";
1414

1515
import { Rewriter } from "../../src";
1616
import { CSSBlocksJSXAnalyzer as Analyzer } from "../../src/Analyzer";
17-
import { makePlugin } from "../../src/transformer/babel";
17+
import { babelPlugin } from "../../src/transformer/babel";
1818
import { testParse as parse } from "../util";
1919

2020
const mock = require("mock-fs");
@@ -43,7 +43,7 @@ function transform(code: string, analysis: Analysis<"Opticss.JSXTemplate">, cssB
4343
let babelResult = babel.transform(code, {
4444
filename: filename,
4545
plugins: [
46-
makePlugin({rewriter}),
46+
babelPlugin({rewriter}),
4747
],
4848
parserOpts: { plugins: [ "jsx" ] },
4949
});

packages/@css-blocks/jsx/test/util.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
import { Analyzer, Options } from "../src/index";
1+
import { Analyzer, AnalyzerOptions, Options } from "../src/index";
22

3-
export async function testParse(data: string, filename = "", opts?: Partial<Options>): Promise<Analyzer> {
4-
let analyzer = new Analyzer("test-analyzer", opts);
3+
export async function testParse(data: string, filename = "", opts?: Options): Promise<Analyzer> {
4+
let analyzerOptions: Options & AnalyzerOptions = opts || {};
5+
analyzerOptions.analyzerName = "test-analyzer";
6+
let analyzer = new Analyzer(analyzerOptions);
57
await analyzer.parse(filename, data);
68
return analyzer;
79
}
810

9-
export async function testParseFile(file: string, opts?: Partial<Options>): Promise<Analyzer> {
10-
let analyzer = new Analyzer("test-analyzer", opts);
11+
export async function testParseFile(file: string, opts?: Options): Promise<Analyzer> {
12+
let analyzerOptions: Options & AnalyzerOptions = opts || {};
13+
analyzerOptions.analyzerName = "test-analyzer";
14+
let analyzer = new Analyzer(analyzerOptions);
1115
await analyzer.parseFile(file);
1216
return analyzer;
1317
}

packages/@css-blocks/webpack/README.md

+5
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ Each template integration's rewriter is slightly different and must be integrate
8181
However, Webpack will typically be used with CSS Blocks' JSX integration. The typical JSX end-to-end integration with webpack looks like this:
8282

8383
```js
84+
const path = require("path");
85+
const appDirectory = fs.realpathSync(process.cwd());
86+
const paths = {
87+
appIndexJs: path.resolve(appDirectory, "src/index.tsx"),
88+
};
8489

8590
const jsxCompilationOptions = {
8691
compilationOptions: {},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/bash
2+
3+
EXAMPLES=jsx
4+
5+
for example in $EXAMPLES; do
6+
cd $example
7+
rm -rf node_modules/@css-blocks
8+
yarn && yarn build || exit 1
9+
cd -
10+
done
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dist/*
2+
!dist/index.html
3+
node_modules
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "hello-world",
3+
"version": "0.0.0",
4+
"private": true,
5+
"main": "index.js",
6+
"license": "MIT",
7+
"dependencies": {
8+
"@css-blocks/jsx": "file:../../../jsx",
9+
"@css-blocks/runtime": "file:../../../runtime",
10+
"@css-blocks/webpack": "file:../../../webpack",
11+
"babel-core": "^6.26.0",
12+
"babel-loader": "^7.1.4",
13+
"babel-preset-env": "^1.6.1",
14+
"babel-preset-react": "^6.24.1",
15+
"babel-preset-stage-2": "^6.24.1",
16+
"file-loader": "^1.1.11",
17+
"obj-str": "^1.0.0",
18+
"react": "^16.3.2",
19+
"react-dom": "^16.3.2",
20+
"typescript": "^2.8.3",
21+
"webpack": "^3.6.9"
22+
},
23+
"scripts": {
24+
"build": "NODE_ENV=development ./node_modules/.bin/webpack"
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# css-blocks-hello-world
2+
3+
React Integration Example
4+
5+
## Run
6+
`npm run build` and open `/dist/index.html`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
:scope {
2+
color: white;
3+
background-color: gray;
4+
}
5+
6+
:scope[state|active] {
7+
background-color: red;
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import React, { Component } from 'react';
2+
import objstr from 'obj-str';
3+
4+
import styles from './Button.block.css';
5+
6+
export default class Button extends Component {
7+
constructor() {
8+
super();
9+
this.state = { isActive: false };
10+
}
11+
12+
toggleIsActive() {
13+
this.setState({ isActive: !this.state.isActive });
14+
}
15+
16+
render() {
17+
const style = objstr({
18+
[styles]: true,
19+
[styles.active()]: this.state.isActive
20+
});
21+
22+
return (
23+
<button className={style} onClick={this.toggleIsActive.bind(this)}>
24+
{this.state.isActive ? "Active" : "Inactive"}
25+
</button>
26+
);
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
:scope {
2+
color: green;
3+
}
4+
5+
.unusedStyle {
6+
background-color: papayawhip;
7+
}
8+
9+
:scope[state|test] {
10+
color: blue;
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
import Button from './Button';
4+
5+
import styles from './index.block.css';
6+
7+
ReactDOM.render(
8+
<div className={styles}>Hello CSS Blocks! <Button /></div>,
9+
document.getElementById('app')
10+
);

0 commit comments

Comments
 (0)