-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Proposal: Library generator publication changes + supporting change to CodeGenerator
#7086
Comments
I recommend we adopt all three proposals. For proposal 1, I have an aesthetic preference for option 1B but concede that option 1A may be clearer and avoids the (I think small but non-zero) possibility of breaking existing generator functions. For proposal 2 I have no strong preference between the options. For proposal 3 I recommend option 3B; it seems like the best compromise between the various goals. |
I would say that in general, it is not safe to copy block generator functions from one As such, I don't really see the advantage of Proposal 1. Is this just a prereq for the other proposals? Wrt proposal 2: The restructuring sgtm. I prefer method 1 for the restructuring because this seems better if you do just want to import individual blocks. However, I'm confused about how you want to add these methods to the dictionary. Are you going to re-key all of the functions? E.g: import {controls_if} from './controls.ts';
import {JavaScriptGenerator} from './generator.ts';
JavaScriptGenerator.blocksGenerators['controls_if'] = controls_if;
// etc... If we want external developers to actually be able to import / use these individually, forcing them to do that seems suboptimal. But I don't really have a better suggestion. Wrt proposal 3: Had we already discussed this when we were renaming the modules? Do you have context for what the conclusion was previously? |
Ahh, no sorry, you misunderstand: I am speaking only of copying from one instance to another of the same language, not between language.
It is a prerequisite to proposal 2, and more broadly to the ability to define and publish generator function without reference to a specific |
This proposal was discussed at our weekly team meeting on 2023-05-16, but no decisions were recorded. |
This implements option 1A of proposal 1 of google#7086. This commit is not by itself a breaking change, except in the unlikely event that developers' custom generator functions take an (optional) second argument of a dfferent type.
…r functions (#7168) * feat(generators): Pass this CodeGenerator to generator functions This implements option 1A of proposal 1 of #7086. This commit is not by itself a breaking change, except in the unlikely event that developers' custom generator functions take an (optional) second argument of a dfferent type. * feat(generators): Accept generator argument in block functions Accept a CodeGenerator instance as parameter two of every per-block-type generator function. * fix(generators): Pass generator when calling other generator functions Make sure to pass generator to any other block functions that are called recursively. * refactor(generators)!: Use generator argument in generator functions Refactor per-block-type generator functions to use the provided generator argument to make recursive calls, rather than depending on the closed-over <lang>Generator instance. This allows generator functions to be moved between CodeGenerator instances (of the same language, at least). This commit was created by search-and-replace and addresses most but not all recursive references; remaining uses will require manual attention and will be dealt with in a following commit. BREAKING CHANGE: This commit makes the generator functions we provide dependent on the new generator parameter. Although CodeGenerator.prototype.blockToCode has been modified to supply this, so this change will not affect most developers, this change will be a breaking change where developers make direct calls to these generator functions without supplying the generator parameter. See previous commit for an example of the update required. * refactor(generators): Manual fix for remaining uses of langGenerator Manually replace remaining uses of <lang>Generator in block generator functions. * fix(generators): Delete duplicate procedures_callnoreturn generator For some reason the generator function for procedures_callnoreturn appears twice in generators/javascript/procedures.js. Delete the first copy (since the second one overwrote it anyway). * chore(generators): Format
This is the third of three issues created to discuss proposed changes to generators (and perhaps track implementation of those proposals, if agreed). This issue concerns proposed changes to the way the generators in
generators/
are published (to form the basis of a standard for the publication of generators as plugins), plus a small breaking change to theCodeGenerator
class incore/generator.ts
to support this. This proposal assumes that the principle parts of the proposals in #7084 and #7085 have been adopted, but is only partially contingent on that.In code examples in these bugs, the word "language" / "Language" / "LANGUAGE" should be read as a metasyntactic variable standing in for some particular language like
JavaScript
.Background
At the moment, the way per-block generator functions are created is via side effects (assigning to a property on a
CodeGenerator
instance), and ties them directly to a particularCodeGenerator
instance (by calling other methods on that specific instance):This means that it is difficult to load just specific generator functions, and it is not generally safe to copy generator functions from one
CodeGenerator
instance to another.Proposal 1: Pass
this
CodeGenerator
when calling generator functionsOption 1A: Pass as parameter
The
CodeGenerator
instance could be passed as a parameter. For backward compatibility it should be the second parameter:Option 1B: Pass as
this
Alternatively the generator instance it could (via
Function.prototype.call
) be passed asthis
:Breaking Changes
This would be a breaking change in
CodeGenerator
, which currently passesblock
asthis
as well as the first parameter, but:this
instead of theblock
parameter.CodeGenerator
instance—which they actually have been until now (though that may change; see Proposal:CodeGenerator
per-block-type generator function dictionary #7084).Proposal 2: Restructure generator modules to contain side effects
At present each language generator consists of three groups of modules:
generators/language.js
creates and exports aCodeGenerator
instance (unstylishly namedLanguage
, despite being an instance and not a class)generators/language/*.js
(excludingall.js
) add methods to theLanguage
instance object.generators/language/all.js
requires all of the above and reexports theLanguage
object. This is the entry point for thelanguage_compressed.js
chunk.Instead, this alternative structure is proposed:
generators/language/generator.ts
(replacinggenerators/language.js
) will declare and exportCodeGenerator
subclass namedLanguageGenerator
, possibly along with additional exports such as anOrder
enum (see proposals in Proposal: Per-language subclasses ofCodeGenerator
ingenerators/
#7085)—but not create alanguageGenerator
instance.generators/language/*.ts
(excludinggenerator.js
) will declare and export per-block generator functions, not referencing any particularCodeGenerator
instance.generators/language.ts
(replacinggenerators/language/all.js
) will import the above, create and export aLanguageGenerator
instance namedlanguageGenerator
, and add the individual generator functions to that instance's dictionary. This file will be the entrypoint for thelanguage_compressed.js
chunk.Rationale for the change
CodeGenerator
instance, largely obviating breaking changes due to this restructuring.LanguageGenerator
instance, and can have more than one such instance—e.g. with different settings and different blocks supported.Option 2A: Export block generator functions individually
In this option each block generator is exported from its module separately:
This option is probably better for ensuring that tree-shaking is successful (where relevant for building minimal compiled bundles), but JS/TS language rules would restrict the block types to valid identifiers (and prohibit 'default', 'this' and other language keywords), and violates the styleguide rules on naming (and this will likely require eslint exceptions).
Option 2B: Export block generators in a dictionary object
In this option all the block generators are exported as properties on a single dictionary object:
This imposes no additional restrictions on block types, but requires that we choose an arbitrary export name (here 'blockGenerators', but suggestions welcome), and may make tree shaking harder. (It could also be construed as being contrary to the styleguide prohibition against namespace objects.)
Proposal 3: Rename the <script> generator entrypoints [UPDATED]
Currently, the recommended way to import e.g. the JavaScript
CodeGenerator
instance is toimport {javascriptGenerator} from 'blockly/javascript';
, but when thejavascript_compressed.js
chunk is loaded via a<script>
tag, thejavascrtiptGenerator
named export (only) is made available viaglobalThis.Blockly.JavaScript
. This was done for backwards compatibility with the originalgoog.provides
implementation, but creates some problems:<script>
orimport
/require
, creating some confusion when discussing generators in our documentation and examples and with developers in the Blockly forum.LanguageGenerator
class constructors,Order
enums, or individual generator functions.Blockly
module (exports) object in a way that would normally be prohibited, and is only possible because we have transpiled it from an ESM to a UMD/CJS module.It is proposed that new bindings be introduced an the existing ones eventually removed after a suitable notice/transition period.
Option 3A: Use a
Blockly.<language>.*
namespaceMake all exports from the entrypoint module (currently
generators/<language>/all.js
, to be renamed togenerators/<language>.ts
under proposal 2, above) available viaglobalThis.Blockly.<language>.*
:javascriptGenerator
object, presently accessible asBlockly.JavaScript
, would be exported asBlockly.javascript.javascriptGenerator
.JavascriptGenerator
class could be exported asBlockly.javascript.JavascriptGenerator
.Order
enum could be exported asBlockly.javascript.Order
.This only partially fixes the naming inconsistency and does not avoid modifying
Blockly
, but avoids any namespace pollution.Option 3B: Use a
<language>.*
namespaceMake all exports from the entrypoint module available via
globalThis.<language>.*
:javascriptGenerator
object, presently accessible asBlockly.JavaScript
, would be exported asjavascript.javascriptGenerator
.JavascriptGenerator
class could be exported asjavascript.JavascriptGenerator
.Order
enum could be exported asjavascript.Order
.This makes
<script src="language_compressed.js">
roughly equivalent toimport * as language from 'blockly/language';
, avoids modifyingBlockly
, and creates only minimal namespace pollution.Option 3C: Add all exports directly to the global scope
Make all exports from the entrypoint module available via
globalThis.*
:javascriptGenerator
object, presently accessible asBlockly.JavaScript
, would be exported asjavascriptGenerator
.JavascriptGenerator
class could be exported asJavascriptGenerator
.Order
enum could be exported asOrder
. (We might wish to chose a more specific name for such exports!)This makes
<script src="language_compressed.js">
roughly equivalent toimport {languageGenerator, LanguageGenrator, /* ... */} from 'blockly/language';
, allowing us to consistently use a barejavascriptGenerator
in our documentation and examples without causing confusion. It avoids modifying theBlockly
module object, but creates considerable namespace pollution—including the potential for clashes between different generators.Breaking Change
Options 3B and 3C introduce new bindings into the global scope, creating the possibility of a clash with any existing developer use of those bindings.
The eventual removal of
Blockly.JavaScript
et al. will obviously break any code that imports generators via<script>
that has not updated to use the new names.Additional Info
Note that this proposal only affects loading via
<script>
, and would be moot if we decide to publish Blockly as ESM rather than UMD/CJS modules: in that case loading Blockly would necessarily always be done viaimport
.The text was updated successfully, but these errors were encountered: