-
Notifications
You must be signed in to change notification settings - Fork 152
feat(css-blocks): Broke up Block.ts, refactored foundational BlockObject constructs, added StateGroup concept. #71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
fb2b0fd
to
d4f9f87
Compare
e483eec
to
e34636b
Compare
packages/css-blocks/package.json
Outdated
@@ -67,6 +67,7 @@ | |||
"css-property-parser": "^1.0.5", | |||
"debug": "^2.6.8", | |||
"inline-source-map-comment": "^1.0.5", | |||
"json-parse-better-errors": "^1.0.1", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why this is here, will check it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I don't think this PR should include any changes to package.json or yarn.lock.
*/ | ||
public setBase(base: Self) { | ||
this._base = base; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should only be allowed on the root. The _base
property is a cache for all other types. We need to find a way to forbid this from being called either through type constraints, moving the method, or failing that a runtime check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved it to Block.ts
, I don't think theres a good way to keep it in inheritable.ts
and restrict it just to root nodes.
if (!this._children.has(name)) { | ||
this.setChild(key, this.newChild(name)); | ||
} | ||
return this._children.get(key) as Child; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use !
to remove the undefined from the return type of the map instead of casting.
return state; | ||
} | ||
getState(name: string): State | null { return name ? this.getChild(name) : this.getChild(UNIVERSAL_STATE); } | ||
resolveState(name: string): State | null { return name ? this.resolveChild(name) : this.resolveChild(UNIVERSAL_STATE); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both getState
and resolveState
require a name, which means that these ternaries on name
are testing for empty string? I'm not a fan of using empty string as a special argument... I think passing no name is better. Also, these methods need some documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 This was a copy pasta mistake I think.
packages/css-blocks/src/query.ts
Outdated
@@ -24,7 +24,8 @@ export class QueryKeySelector implements Query { | |||
this.impl = new QueryKeySelectorImpl(new Element(tag, attrs)); | |||
} | |||
|
|||
execute(container: postcss.Container, block?: Block): ClassifiedParsedSelectors { | |||
return this.impl.execute(container, block); | |||
// Oooohhhh I don't like this! No part of Opticss should know about css-blocks data structures. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. It takes a SelectorFactory
instance which is an abstract interface defined by OptiCSS and which is implemented by the block. removing it from here will hurt performance. If it offends you too much, you can have block hold a reference to a SelectorCache
instance from opticss. I see that you've made some changes to opticss so that parseSelector
has it's own cache. I made comments on that PR. In general, we shouldn't force a particular caching strategy in opticss. By accepting a selector factory, the caller can control how cache eviction will work. Also, because postcss doesn't have any selector parsing ability, it's hard to know if the selector instance has been mutated intentionally and not yet sync'd back to the Rule or is stale.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reverted the selector factory changes to Block
and instead made Inheritable implement SelectorFacotry
so we don't lock the getParsedSelectors
method down only to Block
s. The getParsedSelectors
will fetch from the root's selector cache for any given block object tree.
} | ||
|
||
// TODO: Implement lookup relative to BlockClass. | ||
public lookup(): undefined { return undefined; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's file an issue to track this and remove all the lookup methods with this signature from this PR.
* @param stateName The State's name to ensure. | ||
* @param subStateName Optional state group for lookup/registration | ||
*/ | ||
ensureSubState(groupName: string, subStateName: string): State { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a misnomer now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 removed in favor of other implementation
ca25918
to
e198abe
Compare
…ect constructs, added StateGroup concept. This is a BREAKING CHANGES (obvs).
Fixed linting errors. Plugged typing holes in BlockTree. Air tight now!
- Disentangle Block from opticss - Remove SelectorFactory concept in favor of better caching in parseSelector (see opticss PR) - Comment all BlockTree modules - Add BlockTree tests - Start Style object / RulesetContainer tests - Begin crafting final Node.lookup interface for BlockTrees - Add optional non-silent mode to Node.lookup. If an error occurs during lookup, request an error to be thrown.
After spending considerable time exploring this new structure, I found the BlockTree abstraction mostly obfuscated the connections between core models of the Block (Block, Class, and State). By keeping the Inheritable data type and going directly to the concrete classes without an intermediate abstraction, we're still able to keep the Block types split up, but have less cognitive overhead while navigating the data model.
The source attributes for the state attribute required handling for universal states and also it was no longer including the class's source attributes.
- Inheritable now implements SelectorFactory - setBase is only available on Block objects - Remove LocalScope from project. Not needed after refactor. - Webpack loader exports as default as expected again. - Remove Block's internal OBJ_REF_SPLITTER path parser. - Remove unionInto utility from main package exports.
- New validator ensures no two states from the same state group may be applied. - Fix bug in JSX state group application to analysis. Respects inheritance now. - Add inheritance aware children getters to Inheritable. - Update webpackLoader to have a default export, as expected by Webpack.
b67f16d
to
07863fe
Compare
New core data structure for Blocks
Any Block may be represented by a simple tree structure, called a
BlockTree
. Any tree may be constructed out of two (2) types of nodes:Container
s andStyle
s. Styles are represented byBlockClass
andState
objects and are the primary data we think of when reasoning about Blocks. Styles containRuleset
data and store information about the actual CSS properties defined by the user.BlockClass
andState
objects are owned by theBlock
andStateGroup
Container objects, respectively.BlockTree
s are constructed using the following rules:Block
s will always be the root node of aBlockTree
;Block
s may contain one to mayBlockClass
es;BlockClass
es may contain zero to mayStateGroup
s;StateGroup
s may contain one to mayState
s;Block
andStateGroup
containers always contain, at the very least, the special defaultStyle
s called the "root" class and "universal" state, respectively.The original monolithic
Block.ts
file has been split up into individual files that represent each type of node in a Block tree:Block.ts
,BlockClass.ts
,StateGroup.ts
andState.ts
.These four types of Block classes inherit from the new foundational data structure called
BlockTree
. Block trees define the core behavior forContainer
andStyle
nodes.The core
Container
node class contains all information about a node's type, position in the tree hierarchy, and blocks they inherit from and provide methods for interacting with these relationships.Style
nodes, as mentioned above, are simply extendedContainer
nodes that also hold information about the sourceCSS
styles and may largely be distinguished by the presence of theirRulesetContainer
objects.Because
Container
s andStyle
s are a recursive data model, they have a unique typing definition. Any node type defined for the tree –Block.ts
,BlockClass.ts
,StateGroup.ts
andState.ts
– must be provided types theNode<Self, Root, Parent, Child>
interface.To support these incoming, recursive, Block node typings for all node types, the core
Container
andStyle
definitions use theany
type in some locations. But, don't be fooled! These typing holes are quickly plugged and never exposed outside the BlockTree class definitions, and are never actually exported to the larger project! Insrc/Block/BlockTree/index.ts
we define theSource
,Sink
and regularNode
types for bothContainer
s andStyle
s. Only these classes are actually exported to the outside world, and these definitions plug the few holes in our generic, recursively typed, tree data structure. OnceBlock.ts
,BlockClass.ts
,StateGroup.ts
andState.ts
are exposed to these structures, they only see a fully typed tree data model to interface with.