Making a theme compatible with Drupal core's theme generator tool is very difficult. Theme generator was introduced in 9.3, and in two years the only theme (that I know of) that is clonable is core's starterkit theme.
I believe that the reason is that making a theme compatible is too difficult. Within the issue to make Olivero compatible (#3301173: Allow starterkit theme generator tool to clone Olivero), we need 240 lines of fairly complicated PHP. This is unreasonable for a themer to do, and most don't have the necessary skillset to do so.
Plan
This MR introduces a new way to have the generate-theme command clone your theme. The command now will look for a theme_name.starterkit.yml file within the root of the theme. If the file exists, it will assume the theme is cloneable (this replaces the starterkit: true within the theme’s info.yml file).
The theme generator command will loop through all files and filenames and do a string replace of the machine_name of the starterkit theme with the machine_name of the new theme. You can change the behavior of this with the contents of the new theme_name.starterkit.yml, in which you can declare the following:
ignore- Files to ignore (not copy over). Paths can be wildcards (e.g.*.yml) or globs (e.g.**/*.js)no_edit- Files where the contents will not be edited. Paths can be wildcards (e.g.*.yml) or globs (e.g.**/*.js)no_rename- Files that will not have their filename renamed. Paths can be wildcards (e.g.*.yml) or globs (e.g.**/*.js)info- Additional YML that will be placed in the new theme’s info.yml file.
Testing
You can test this by adding a theme_name.starterkit.yml to your theme, and verifying all of the functionality of the keys above.
Issue fork drupal-3364885
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3364885-making-a-theme
changes, plain diff MR !4121
Comments
Comment #2
andy-blumI plan to work on this at DrupalCon contrib and welcome others to share thoughts for how to make this simpler.
Comment #4
andy-blumMR #4121 is currently a work in progress, but attempts to simplify the starterkit adoption by other themes by introducing support for a new yml file, *.starterkit.yml. This file in the starterkit_theme looks like the below:
The delete key specifies a list of files, directories, or globs to delete from the temporary working directory of the process. In the above example, the files related to making the theme a starterkit are removed
The no_edit key specifies a list of files, directories, or globs to avoid changing the internal contents of.
The no_rename key is similar, but prevents files from being renamed.
The info key specifies keys that should be changed from the current theme's info files. Keys can be added or changed in this file simply by setting their values, and they can be removed by setting their values to null.
Comment #5
lauriiiFor context, I'm +1 for this. The starterkit was built the way it is because it was built for a single purpose and we realized later that there could be benefit in allowing other themes to use it. I think makes sense to build an API like this to simplify the experience.
Comment #6
andy-blumComment #7
dave reidI am also +1 on this, we experienced a large amount of "manual work required afterwards" with a custom base theme we provide in a distribution install profile for D9/10 and I think this would help alleviate it.
Comment #8
andy-blumTest failures are saying it can't find the Symfony Filesystem component. Is this because that's only a dev dependency?
https://git.drupalcode.org/project/drupal/-/blob/11.x/composer.json#L37-38
Comment #9
smustgrave commentedThat is correct. On line 38 of GenerateThemeTest it does a composer install with --no-dev.
Also running the test locally without the --no-dev I still get a failure
Comment #10
andy-blumBlocked by #3369120: Promote symfony/finder from dev-dependency to full dependency and #3369121: Promote symfony/filesystem from dev-dependency to full dependency
Comment #11
andy-blumRan this as recommended by the test results, but it failed with this:
Comment #12
lauriiiComment #13
camoa commentedI think the starterkit YAML file is a great addition, will it be worth to also provide a simple way to pass options? For dynamic changes the theme may need, for example, it could allow.Olivero to set the default color of the generated theme, or allow themes to provide basic options for customization and avoid manual changes.
It could be as simple as passing a --options to the generate theme command and pass this along to the starterkit postprocess.
Comment #14
isholgueras commentedIf, as a developer, wants to run unit tests, you need to require, as a dev dependency, the package
drupal/core-dev.The package drupal/core-dev brings symfony/finder and symfony/filesystem ^6.3: https://github.com/drupal/core-dev/blob/11.x/composer.json#L32-L33
As this funcionality seems to be for developers, could make sense to require, as a dev dependency, the drupal/core-dev package?
Comment #15
mglamanDiscussed with @andy-blum and I think we can refactor this to not require the Symfony dependencies. This script is a development tool. But it is executable without the dependency existing whereas things like PHPUnit tests are not. I think it'd be easier to land without using the Symfony components and avoid debating this situation.
I'm going to give it a shot.
Comment #16
mglamanI ran out of time today. The hardest part is recreating this with
globor the proper iterators:Comment #17
mherchelThe core committers seem willing to introduce the Symfony dependencies. Is there a reason (either way) to introduce/not introduce them?
If the code is really complicated, does this help make the case for including the dependencies?
Thanks for working on this btw!
Comment #18
andy-blumThe reason for trying to avoid the new dependencies was to speed this along.
I would argue yes.
Comment #19
mherchel@mglaman - Did you save your progress on this? if so can you post a patch? I have someone that is willing to pick up and run with it.
Comment #20
kostyashupenkoMy 50 cents regarding theme generator.
1. Theme generator should scan all folders and files inside of starterkit theme (excluding of course some trash like node_modules, etc)
2. Theme generator should be sensitive to the text cases. Somehow it should understand "my_theme", "myTheme", "my-theme", etc text cases
3. Theme generator should understand that sometimes it shouldn't change name. Ex. - sometimes in `package.json` in theme we can have specific npm packages which are partially contains sub-name of the starterkit name -> obviously name of npm package should not be changed (but for example "name" property in package.json should be changed).
4. Back to first point - starterkit theme can contain js files with drupal behaviors. And behavior name can include name of the starterkit theme -> `MyStarterkitThemeMySuperBehavior` <--- here theme generator script should change part of behavior name to the generated theme name.
Now.. how we can deal with all these?
We are using hygen node script to handle generation process from the starterkit theme. It allows us to put conditional comments everywhere where we need regarding what to change, where, which text case, etc etc etc.
So this process is automated through the "hygenic" comments.
Comment #21
wim leersThis is blocking #3301173: Allow starterkit theme generator tool to clone Olivero, so matching issue metadata.
Comment #22
wim leersOh wow, there's two blockers?! 😳
Comment #23
matthieuscarset commentedMy 2cents: while working on a new piece of code to add into the Starterkit theme, I needed to replace mentions of starterkit_theme_ in more places:
/js/*.jsfiles (see this line)I cross-link these changes here before I revert them as I was told better not to touch the Theme Generator because of the ongoing work on this present issue.
Comment #24
andy-blumSymfony's finder & filesystem components are now full runtime dependencies [CR]
Comment #25
andy-blumTest failures are now related to the generate theme command. Abridged failures below:
Comment #26
fjgarlin commentedThe problem is that the new private method
overrideThemeInfois returning a 1, which means an error happened, but this is not being captured at all in this line (https://git.drupalcode.org/issue/drupal-3364885/-/blob/3364885-making-a-...), therefore it continues and runs all the way to line 225.So it seems like the new refactoring should read the error and bail out in that case.
Comment #27
andy-blumComment #28
mherchelThanks for working on this! I tested this out and ran into a number of issues:
1. I'm unable to delete entire directories.
I declared
starterkit: truein Olivero and tried to delete thetestsdirectory by addingOr other permutations, and wasn't able to get it to succeed.
2. If nothing found under
no_editorno_rename, no error is thrownI expect an error to be returned if the process doesn't find any files that it can operate against
3. Wildcards / globs don't work
I tried passing in
and it still edits the files.
3. No longer any need for
starterkit: truein the YML fileThe presence of a themeName.starterkit.yml is indication enough
4. Nice to have: debug mode
It'd be super nice if this process could have a debug mode to help out theme developers.
Comment #29
jwilson3Can confirm #28.1. the error message is:
I also noticed that the no_edit and no_rename are not working as expected:
Running:
I expected to see file: themes/custom/sub_theme/js/parent-init.js but instead saw themes/custom/sub_theme/js/sub_theme-init.js and I expected to see the file copied in place as-is:
But instead saw:
Comment #30
mglamanI've been working on this @ FLDC. Taking a break but keeping it assigned. Most bugs addressed, and tests added, but needs some TLC refactoring
Comment #31
mglamanComment #32
ctrladelI helped with Matt's globbing at Drupal's second best camp
Comment #33
mherchelNO CREDIT FOR THIS MAN!!!!
Comment #34
mglamanI wasn't able to finish before leaving FLDC but I'd say it's 80%. There a few things I want to take care of to tighten up the code and there is one of two missing test cases.
It is manually testable though. I'd hold on code reviews until I can finish my changes. I'll work on them in the next few days
Comment #35
mglamanUnassigning for now. I'm not moving to NR yet because the tests will fail, as I left an incomplete test
But it is ready for general review and testing.
Comment #36
mglamanReady for review! I wrote the last test and did my final bits of refactoring.
Comment #37
mherchelIn the process of doing an in-depth review.
Setting to NW now, because I found a blocking bug.
I copied and pasted the starterkit.yml from starterkit_theme into Olivero just to give it a run
The theme generated fine. When going to admin/appearance, there was an error:
Sure enough, Olivero has
base theme: false(on line 15) in its info.yml file. The new theme did not have this, which causes the error. If I go into Olivero and set the base theme to something else, it copies over. So I'm assuming it's not copying because it's set tofalseComment #38
mglamanAh, I know why https://git.drupalcode.org/project/drupal/-/merge_requests/4121#note_275423 good find
Comment #39
mherchelAnother bug:
The starterkit_theme.starterkit.yml contains a
deletekey. It looks like this has been replaced byignoreComment #40
mherchelOverall this is working great! Not finding any more ways to break it (so far!).
no_renamea file that doesn't exist I get a[ERROR] Paths were defined `no_rename` but no files found.(same behavior forno_edit)A couple questions:
no_renameandno_edit, however theignorekey just skips over it and doesn't have the same behavior. Is it possible to make the behavior consistent? This shouldn't be a blocker for this issue.Comment #41
mglamanAddressing feedback. And adding verbose output. So if someone passed
-vit displays verbose messaging.Comment #42
mglamanAddressed findings and added a test for
falsebeing removed. Added extra messages which are displayed when-vvvis passed for debug output.Comment #43
mherchelThis is amazing!
base theme: falsenow copies over as expected.ignorekey, an error is thrown.Question:
Currently if at least one file exists under
ignore(orno_editorno_rename) the script will not error out. However this is confusing, because there might be a mix of existing and non-existent files. Is it possible to error if any of the listed files are not found? This isn't a blocker, but setting to NW to see if this can be done.Comment #44
mglamanThat'd be really hard to figure out, especially if they're using wildcards/globs. We could do the check if no wildcard was found in the string though
Comment #45
mherchelMakes sense. That's mostly just a nice-to-have. Setting to RTCB
Comment #46
mherchelUpdating IS
Comment #47
mherchelComment #48
mherchelComment #49
mglamanBlah, I broke tests with the debug output.
Comment #50
mglamanPushed a commit to fix the tests 🤞
Comment #51
mglamanTook a stab at the CR https://www.drupal.org/node/3425844
Comment #52
mherchelTests are passing. Back to RTBC.
Comment #53
quietone commentedTrying for a more informative title and commit message.
Comment #54
andy-blum@mglaman - It might be worth fleshing out the bullet point about the info.yml file. Specifically, that you can override existing values and *remove* values with `null`.
Comment #55
mglaman@andy-blum updated CR
Comment #57
alexpottJust some small things on the MR. Looks really good.
Comment #58
mglamanAddressed feedback. Left one comment.
Comment #59
mglamanRemaining item addressed. Failure is on Drupal.Tests.Composer.Plugin.Scaffold.Functional.ManageGitIgnoreTest:: testUnmanagedGitIgnoreWhenGitNotAvailable which is unrelated.
Comment #60
mherchelPulled down latest changes and went through some general testing and everything still works. Feedback appears to have been addressed 🙌
Comment #61
mherchelAdjusting credits (please feel free to tweak).
Comment #62
alexpottCommitted and pushed 6c2ed72990 to 11.x and d82e52c0cf to 10.3.x. Thanks!
Comment #65
amber himes matzThis is great! Please create a change record that will help folks update docs and tutorials.
Comment #66
mglamanOne was created and published when the MR was merged: https://www.drupal.org/node/3425844
Comment #68
mlncn commentedThis is awesome. Created a starterkit theme. Only issue is that any "dot" file was not copied over— .gitignore, .nvmrc —and these are pretty useful files for the new themes. Is this intentional or an oversight? Is there a way to override the behavior?
Comment #69
mglaman@mlncn can you open a new issue? "Dotfiles ignored when copied"
Comment #70
joachim commentedThis parameter gets added with no documentation.
Comment #71
joachim commentedAND it's a parameter that's only used in tests??? That's not a good pattern.
Comment #72
doxigo commentedHaving the same issue on Radix with the Dotfiles not getting copied over.
Comment #73
f0ns commentedIs there a way to exclude some strings to be replaced.
For example you have:
lollapalooza-cms and the initial theme was lollapalooza.
If I generate a new theme named new-theme
I notice lollapalooza-cms is replaced with new-theme-cms but I would prefer if the occurences of lollapalooza-cms is not touched anywhere.
Comment #74
f0ns commentedI haven't found a way to exclude strings to be replaced by default.
So I now use a quick and dirty workaround, in the post process function I have an array of "protected words". They get switched back after they were replaced.
The post process function runs after the replacement. It would be nice to have a way to fix this before any replacement.