-
Notifications
You must be signed in to change notification settings - Fork 153
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
Extended @block
Reference Syntax
#192
Comments
@block
Reference Syntax@block
Reference Syntax
Dumb question. Can you have multiple exports of a block file? It will by default export
In the case above only the |
You would have to explicitly import /* block-2.block.css */
@block block-1, ( local-block-1 ) from "block-1.block.css";
/* `local-block-1` is undefined */ Instead, if you import /* block-2.block.css */
@block block-1, ( local-block-1 ) from "block-1.block.css";
/*
`local-block-1` is defined and set to the default export from `block-foo.css`,
which was re-exported by `block-1.block.css` as `local-block-1`
*/ |
Why wouldn't this be an error? |
We can totally make it one, was opting for flexibility – I can see a scenario where devs want to test importing a block from somewhere else under the same name and instead of removing the old block reference, they just add another block ref under the original for testing in dev. In this case it can also be a warning. I'm fine making it an error if dev time flexibility is not a strong enough argument. |
@amiller-gh This issue doesn't give a motivation for the need to re-export blocks that are referenced. If the default block extends or implements other blocks, it becomes the superset interface for those blocks and the consumer of that block does not need access to the blocks that it extends or implements (and, in-fact, it would be incorrect to resolve against the base-block directly). |
It makes creating component modules that need expose multiple blocks easy, especially for composable components. Consider our hovercard component, which needs to deliver both /* styles/container.block.css */
:scope { ... } /* styles/trigger.block.css */
:scope { ... } /* styles/index.block.css */
@block trigger from "./trigger";
@block container from "./container"; Consumers that need to then import the blocks can do so like this: @block ( container, trigger ) from "@linkedin/hovercard"; |
Alternatively, if there is an obvious "primary" block for a module, the /* styles/trigger.block.css */
:scope {
block-name: "hovercard-trigger";
...
} /* styles/index.block.css */
@block trigger from "./trigger";
:scope {
block-name: "hovercard";
...
} Consumers that need to then import the blocks can do so like this: @block hovercard, ( trigger ) from "@linkedin/hovercard"; |
@amiller-gh ok. That's what I thought it might be. I'm not sure that a block is the right abstraction for this. Maybe you've already thought this part through but I'd like to explore:
|
I like errors. They are clear and they keep me from doing dumb things and being confused about them. If I lose 1-2 min to scratching my head in confusion, it eats up a lot of the savings of not having to do an easy thing that only saves 5-10 seconds. |
@chriseppstein the power of RFCs at work. You're right. Let me play counterpoint for a moment though. I could make the argument (and I will, watch me) that removing resolutions between any internal blocks is in fact a breaking change. Consider: /* node_modules/@module/foo/styles/a.block.css */
:scope {
color: red;
} /* node_modules/@module/foo/styles/b.block.css */
@block a from "./a.block.css";
:scope {
color: blue;
color: resolve(a);
} /* app/styles/main.block.css */
@block ( a, b ) from "@module/foo"; {{!-- app/templates/main.hbs --}}
<div class="a b">What color am I?</div> In the above code snippet, the app is relying on the resolution of blocks This seems like an easy kind of change for module maintainers to gloss over and not realize they're breaking their consumers. By giving |
@amiller-gh I wasn't arguing that removing a An explicit |
It would codify that, but I don't think it should. There's no need for a consumer of a block to ever hold a direct reference to a block's parent block nor to the block interfaces that it implements, the block itself is the correct point of access to the local names of that block. |
I don't disagree, was largely just trying to give the concept its time in court 😉 Will think through and add to the writeup what an explicit export syntax looks like. You're right, it minimizes API surface area. |
ammiller [3:04 PM] ceppstein [3:05 PM] ammiller [3:06 PM] ceppstein [3:07 PM] ammiller [3:07 PM] ammiller [3:09 PM] ceppstein [3:10 PM] ammiller [3:10 PM] ceppstein [3:25 PM] ammiller [3:33 PM] ceppstein [4:07 PM] ammiller [4:18 PM] ceppstein [4:19 PM] |
Not sure how we could scope the block name without referencing individual blocks from a fully qualified npm path – and at that point you're a couple small syntactic sugar features away from the destructured import syntax. What were you thinking here @chriseppstein? |
- Rename @block-reference to @block - Default @block imports from a Block module. - Named @block imports from a Block module. - Default Block exports from a module. - Named Block exports from a module. - default as a reserved name. - Introduced simple parser for import/export parsing. - Split apart parser tests into logical packages.
- Rename @block-reference to @block - Default @block imports from a Block module. - Named @block imports from a Block module. - Default Block exports from a module. - Named Block exports from a module. - default as a reserved name. - Introduced simple parser for import/export parsing. - Split apart parser tests into logical packages.
Summary
Implementation of this proposal will enable the following features:
@block-reference
to@block
@block
import syntax@block
imports from a Block module@block
imports from a Block moduledefault
as a reserved namecss-blocks
package.json config fieldnode_module
Block import path resolutionDetailed design
Rename
@block-reference
to@block
After some time out in the wild, we've received feedback that typing
@block-reference
is onerous and looks weird.This proposal would update
@block-reference
:To
@block
:This has the added benefit of providing the option of backwards compatibility / deprecation for this feature change, although we are pre-1.0 and this is not required.
@block
ImportsTo enable a more robust module delivery system in css-blocks, this proposal would define the API options for
@block
as follows.<block-import>
<blocks-list>
" from "<block-path>
<blocks-list>
<default-block>
|<named-blocks>
|<default-block>
" , "<named-blocks>
<default-block>
<ident>
<named-blocks>
<named-ref>
{ " , "<named-ref>
} " ) "<named-ref>
<local-name>
|<aliased-block>
<aliased-block>
<ident>
" as "<local-name>
<local-name>
<ident>
(Note: Lexer restriction – cannot be "default")<block-path>
<any-value>
' " ' | " ' "<any-value>
" ' "Examples of this grammars use are
Default
@block
imports from a Block moduleIn the above example, the default exported block from the block file discovered at
<block-path>
will be available under the local namelocal-name
.Named
@block
imports from a Block moduleTo import a named export of a Block file:
@block
ExportsDefault Block exports from a module
All rules defined in the scope of a Block file are the
default
export of a block. So, the following file exposes a single block calleddefault
which contains the BlockClasses:scope
and.foo
when imported elsewhere:Even if there are no declarations in a Block file, there will always be a default block exported.
Named Block exports from a module
Any imported Blocks that need to be made available from the Block file as named exports must be explicitly re-exported. A similar syntax to
@block
imports is used.default
as a reserved wordTo avoid naming conflicts and import/export ambiguity, user will be unable to import or export blocks under the local name
default
. If this were allowed, it would become ambiguous how the importeddefault
block would interact with the local Block definition, also calleddefault
. The following would throw a build time error of "Error: 'default' is a reserved word.":Execution order and name conflicts
@block
imports are statically defined (I mean, what else is CSS good for) and parsed in order of appearance before the default Block's contents are parsed. Because of this, you can imagine that all@block
references are hoisted at runtime and processed first, regardless of location in the file.Local identifier values will be assigned Block references in order of execution. Duplicate identifiers will have their values overwritten.
css-blocks
package.json config fieldModules that deliver Block files may choose to add an optional
css-blocks: { ... }
configuration field to theirpackage.json
s. Here they may define configuration options for their module. At the time or writing, there is only one valid property for the options hash:ModuleRoot
CSS Blocks module resolution algorithm
import X from module at path Y
a. return the core module
b. STOP
a. set Y to be the module root (closest package.json)
a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X)
LOAD_AS_FILE(X)
*.block.css
file, load X as a Block. STOPLOAD_INDEX(X)
LOAD_AS_DIRECTORY(X)
a. Parse X/package.json, and look for "css-blocks.main" field.
b. let M = X + (json main field)
c. LOAD_AS_FILE(M)
d. LOAD_INDEX(M)
LOAD_NODE_MODULES(X, START)
a. LOAD_AS_FILE(DIR/X)
b. LOAD_AS_DIRECTORY(DIR/X)
NODE_MODULES_PATHS(START)
a. if PARTS[I] = "node_modules" CONTINUE
b. DIR = path join(PARTS[0 .. I] + "node_modules")
c. DIRS = DIRS + DIR
d. let I = I - 1
Outstanding Questions
The text was updated successfully, but these errors were encountered: