Skip to content

Commit

Permalink
bento jss: transform the CSS and useStyles exports. (#29777)
Browse files Browse the repository at this point in the history
* build-system: teach buildExtension how to build jss

* remove style-loader from main deps

* make work with watch mode

* add babel transform

* delete jss build, perform at babel time

* lint

* dd todo

* also convert the module.exports into named exports

* copyright

* Thank you justin, path.evaluate is gold.

* Update with smarter xform

* extract classes var, no longer jss required export

* another test

* remove rest operator

* fix, bad interpolation

* improve two comments

* lint

* updates, tanks justin

* lint

* Update build-system/babel-plugins/babel-plugin-transform-jss/index.js

Co-authored-by: Justin Ridgewell <justin@ridgewell.name>

* Update build-system/babel-plugins/babel-plugin-transform-jss/index.js

Co-authored-by: Justin Ridgewell <justin@ridgewell.name>

* remove outdated option

* lint

* remove computer properties for browserify

* Allow JSON.parse in jss files for conformance-config

* test code also needs transform

Co-authored-by: Justin Ridgewell <justin@ridgewell.name>
  • Loading branch information
samouri and jridgewell committed Aug 26, 2020
1 parent cea6c61 commit 6f97154
Show file tree
Hide file tree
Showing 26 changed files with 485 additions and 53 deletions.
1 change: 1 addition & 0 deletions build-system/babel-config/pre-closure-config.js
Expand Up @@ -66,6 +66,7 @@ function getPreClosureConfig() {
'./build-system/babel-plugins/babel-plugin-is_minified-constant-transformer',
'./build-system/babel-plugins/babel-plugin-transform-amp-extension-call',
'./build-system/babel-plugins/babel-plugin-transform-html-template',
'./build-system/babel-plugins/babel-plugin-transform-jss',
'./build-system/babel-plugins/babel-plugin-transform-version-call',
'./build-system/babel-plugins/babel-plugin-transform-simple-array-destructure',
replacePlugin,
Expand Down
1 change: 1 addition & 0 deletions build-system/babel-config/test-config.js
Expand Up @@ -60,6 +60,7 @@ function getTestConfig() {
replacePlugin,
'./build-system/babel-plugins/babel-plugin-transform-json-configuration',
'./build-system/babel-plugins/babel-plugin-transform-fix-leading-comments',
'./build-system/babel-plugins/babel-plugin-transform-jss',
'./build-system/babel-plugins/babel-plugin-transform-promise-resolve',
'@babel/plugin-transform-react-constant-elements',
'@babel/plugin-transform-classes',
Expand Down
1 change: 1 addition & 0 deletions build-system/babel-config/unminified-config.js
Expand Up @@ -44,6 +44,7 @@ function getUnminifiedConfig() {
const unminifiedPlugins = [
replacePlugin,
'./build-system/babel-plugins/babel-plugin-transform-json-configuration',
'./build-system/babel-plugins/babel-plugin-transform-jss',
'./build-system/babel-plugins/babel-plugin-transform-fix-leading-comments',
'./build-system/babel-plugins/babel-plugin-transform-promise-resolve',
'@babel/plugin-transform-react-constant-elements',
Expand Down
Expand Up @@ -16,6 +16,7 @@
registerServiceBuilder(win, 'story-analytics', function () {
return new StoryAnalyticsService();
});

geoDeferred = new Deferred();
AMP.registerServiceForDoc('foo', function(){return geoDeferred.promise;});
AMP.registerServiceForDoc('foo', function () {
return geoDeferred.promise;
});
107 changes: 107 additions & 0 deletions build-system/babel-plugins/babel-plugin-transform-jss/index.js
@@ -0,0 +1,107 @@
/**
* Copyright 2020 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* Takes a .jss.js file and transforms the `useStyles` export to remove side effects
* and directly return the classes map. Also includes special key 'CSS' in the classes
* object with the entire CSS string.
*
* @example
* In:
* ```
* import {createUseStyles} from 'react-jss'
*
* const jss = { button: { fontSize: 12 }}
* export const useStyles = createUseStyles(jss);
* ```
*
* Out:
* ```
* const jss = { button: { fontSize: 12 }}
* const _classes = {button: 'button-1', CSS: 'button-1 { font-size: 12 }'}
* export const useStyles = () => _classes;
* ```
*/

const {create} = require('jss');
const {default: preset} = require('jss-preset-default');

module.exports = function ({types: t, template}) {
function isJssFile(filename) {
return filename.endsWith('.jss.js');
}

function compileJss(JSS) {
const jss = create();
jss.setup(preset());
return jss.createStyleSheet(JSS);
}

return {
visitor: {
CallExpression(path, state) {
// TODO: Can I skip the whole file if not jss?
const {filename} = state.file.opts;
if (!isJssFile(filename)) {
return;
}

const callee = path.get('callee');
if (!callee.isIdentifier({name: 'createUseStyles'})) {
return;
}

const {confident, value: JSS} = path.get('arguments.0').evaluate();
if (!confident) {
throw path.buildCodeFrameError(
`First argument to createUseStyles must be statically evaluatable.`
);
}
const sheet = compileJss(JSS);
if ('CSS' in sheet.classes) {
throw path.buildCodeFrameError(
'Cannot have class named CSS in your JSS object.'
);
}

const id = path.scope.generateUidIdentifier('classes');
path.scope.push({
id,
init: template.expression.ast`JSON.parse(${t.stringLiteral(
JSON.stringify({
...sheet.classes,
'CSS': sheet.toString(),
})
)})`,
});

path.replaceWith(template.expression.ast`(() => ${id})`);
},

// Remove the import for react-jss
ImportDeclaration(path, state) {
const {filename} = state.file.opts;
if (!isJssFile(filename)) {
return;
}

if (path.node.source.value === 'react-jss') {
path.remove();
}
},
},
};
};
@@ -0,0 +1,19 @@
/**
* Copyright 2020 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {createUseStyles} from 'react-jss';
const JSS = {button: {fontSize: 12}};
export const useStyles = createUseStyles(JSS);
@@ -0,0 +1,5 @@
{
"plugins": ["../../../.."],
"sourceType": "module",
"filename": "component.js"
}
@@ -0,0 +1,22 @@
/**
* Copyright 2020 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createUseStyles } from 'react-jss';
const JSS = {
button: {
fontSize: 12
}
};
export const useStyles = createUseStyles(JSS);
@@ -0,0 +1,19 @@
/**
* Copyright 2020 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// Throws because cant use classname "CSS", as it conflicts with this transform.
import {createUseStyles} from 'react-jss';
export const useStyles = createUseStyles({CSS: {fontSize: 12}});
@@ -0,0 +1,6 @@
{
"plugins": ["../../../.."],
"sourceType": "module",
"filename": "component.jss.js",
"throws": "CSS"
}
@@ -0,0 +1,21 @@
/**
* Copyright 2020 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// Throws because object spread is not statically evaluable.
import {createUseStyles} from 'react-jss';
const foo = {foo: 7}
const JSS = {...foo}
export const useStyles = createUseStyles(JSS);
@@ -0,0 +1,6 @@
{
"plugins": ["../../../.."],
"sourceType": "module",
"filename": "component.jss.js",
"throws": "statically evaluatable"
}
@@ -0,0 +1,20 @@
/**
* Copyright 2020 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {createUseStyles} from 'react-jss'

const JSS = {button: {fontSize: 12}};

export const useStyles = createUseStyles(JSS);
@@ -0,0 +1,5 @@
{
"plugins": ["../../../.."],
"sourceType": "module",
"filename": "component.jss.js"
}
@@ -0,0 +1,23 @@
var _classes = JSON.parse("{\"button\":\"button-0-2-1\",\"CSS\":\".button-0-2-1 {\\n font-size: 12px;\\n}\"}");

/**
* Copyright 2020 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const JSS = {
button: {
fontSize: 12
}
};
export const useStyles = () => _classes;
@@ -0,0 +1,19 @@
/**
* Copyright 2020 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {createUseStyles} from 'react-jss';

export const useStyles = createUseStyles({button: {fontSize: 12}});
@@ -0,0 +1,5 @@
{
"plugins": ["../../../.."],
"sourceType": "module",
"filename": "component.jss.js"
}
@@ -0,0 +1,18 @@
var _classes = JSON.parse("{\"button\":\"button-0-3-1\",\"CSS\":\".button-0-3-1 {\\n font-size: 12px;\\n}\"}");

/**
* Copyright 2020 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const useStyles = () => _classes;
@@ -0,0 +1,19 @@
/**
* Copyright 2020 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

const runner = require('@babel/helper-plugin-test-runner').default;

runner(__dirname);
4 changes: 4 additions & 0 deletions build-system/tasks/storybook/preact-env/webpack.config.js
Expand Up @@ -58,6 +58,10 @@ module.exports = {
],
},
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
};
1 change: 1 addition & 0 deletions build-system/test-configs/conformance-config.textproto
Expand Up @@ -398,6 +398,7 @@ requirement: {
whitelist: 'ads/zen.js'
whitelist: 'extensions/amp-viewer-integration/0.1/messaging/messaging.js' # published as standalone library
whitelist: 'src/json.js' # Where parseJson itself is implemented.
whitelist_regexp: '.*\\.jss\\.js'
}

requirement: {
Expand Down

0 comments on commit 6f97154

Please sign in to comment.