Czy można używać @wordpress/create-block z wieloma blokami?
To jest! Bawiłem się tym z przerwami przez ostatni rok i wymyśliłem kilka różnych sposobów, aby to osiągnąć. Są to jednak tylko produkty moich własnych manipulacji – mogą istnieć bardziej przekonujące rozwiązania. Biorąc pod uwagę kierunek @wordpress/scripts
rozwoju, spodziewałbym się, że ten przypadek użycia stanie się łatwiejszy w przyszłości.
NOTATKA: Jeśli zamierzasz zgłosić swoje bloki do umieszczenia w publicznym katalogu bloków, (wprowadzenie) aktualne wytyczne określają, że wtyczka powinna udostępniać tylko jeden blok najwyższego poziomu. Wszelkie dodatkowe bloki powinny być blokami podrzędnymi i określać ich związek z blokiem najwyższego poziomu poprzez
parent
pole w odpowiednim dla siebie polublock.json
akta.
@wordpress/scripts
to abstrakcja narzędzi do budowania i programowania, która jest używana we wtyczkach rusztowanych przez @wordpress/create-block
aby między innymi uprościć transpilację JS, kompilację SCSS i kod lintingowy pod kątem błędów. Zawiera babel
, webpack
I eslint
aby wymienić tylko kilka.
Pakiet jest również przydatny jako zależność rozwojowa wtyczek i motywów, które nie oferują bloków; ten sam proces konfiguracji można zastosować poniżej, aby lepiej dostosować narzędzia do kompilacji do potrzeb rozszerzenia i struktury plików.
Domyślna konfiguracja kompilacji
Najpopularniejszym mechanizmem zmiany dowolnego aspektu tworzenia zasobów wtyczki jest modyfikacja lub wymiana konfiguracji pakietu internetowego dostarczonej przez @wordpress/scripts
pakiet, jak krótko wspomniano w pliku README.
Aby zorientować się, jaka jest struktura i ustawienia domyślnego obiektu konfiguracyjnego, możemy odwołać się do źródła skryptu wp-script webpack.config.js
plik. Główne ustawienia, którymi się zajmujemy, to entry
I output
obiekty, które określają, które pliki służą jako punkty wejścia do Twojego kodu i gdzie są kompilowane ich zasoby, odpowiednio:
// ...
entry: {
index: path.resolve( process.cwd(), 'src', 'index.js' ),
},
output: {
filename: '[name].js',
path: path.resolve( process.cwd(), 'build' ),
jsonpFunction: getJsonpFunctionIdentifier(),
},
// ...
Powyżej widzimy, że wp-scripts określa pojedynczy punkt wejścia o nazwie index
zlokalizowany w ./src/index.js
i tworzy pakiet JavaScript dla każdego punktu wejścia w ./build/[name].js
(gdzie tutaj, index
zostałby zastąpiony [name]
dla naszego pojedynczego punktu wejścia).
Wiele innych zasobów jest również generowanych przez różne skonfigurowane wtyczki i programy ładujące.
Zastępowanie konfiguracji pakietu internetowego
Zastępowanie wartości domyślnej webpack.config.js
jest proste – po prostu tworzymy plik o tej samej nazwie w katalogu głównym naszego projektu, a skrypty wp go rozpoznają i użyją.
Aby zmodyfikować konfigurację, możemy albo zaimportować obiekt konfiguracyjny pakietu Webpack wp-scripts, zmodyfikować go i ponownie wyeksportować zmodyfikowany obiekt, albo całkowicie wyeksportować nowy obiekt konfiguracyjny, aby całkowicie zastąpić skrypty wp-scripts.
NOTATKA: Popularną konwencją jest używanie wzorca modułu CommonJS i nowszej składni ECMAScript podczas zapisywania pliku konfiguracyjnego pakietu Webpack, ponieważ plik ten zwykle nie jest transponowany na bardziej globalny standard. Dzięki temu programiści korzystający z nowszych silników Node.js mogą również tworzyć kod.
Struktura pliku
Większość poniższych rozwiązań zakłada strukturę źródłową zawierającą dwa bloki (foo
I bar
) w ./src/blocks
katalog obok nieblokowego zasobu JS (./src/frontend/accordion.js
), o ile nie zaznaczono inaczej. W szczególności skonfigurowałem je pod kątem struktury projektu według moich własnych preferencji:
Kopiowanie zasobów z katalogów źródłowych
W moich rozwiązaniach poniżej używam CopyWebpackPlugin
aby skopiować każdy blok block.json
plik do katalogu wyjściowego. Dzięki temu można ich używać bezpośrednio z katalogu wyjściowego przy użyciu ścieżek względnych do zasobów, które mają sens w każdym kontekście. Jak @wordpress/scripts
korzysta obecnie z Webpacka 4 (choć nie na długo), na razie będziesz potrzebować wersji 6 wtyczki:
npm install --save-dev copy-webpack-plugin@6
Alternatywnie możesz załadować plik block.json
pliki z ./src
i używaj dużych ścieżek względnych, aby wskazać zbudowane zasoby (np "editorScript": "file:../../../build/blocks/foo/index.js"
) lub być może zbierz je w katalogu głównym projektu o nazwie as block-{block name}.json
lub podobne.
Ładowanie bloków z nowej struktury
W większości przypadków możesz po prostu przekazać ścieżkę pliku do każdego bloku block.json
plik do a register_block_type_from_metadata()
wywołaj główny plik wtyczki swojego projektu głównego.
PHP odnoszące się do konkretnych bloków, w tym rejestracji bloków, można również pozostawić w katalogu źródłowym bloku i albo zaimportować bezpośrednio stamtąd, albo skopiować do katalogu wyjściowego.
Rozwiązanie wielu projektów blokowych
Najprostsze rozwiązanie skutkuje nieco skomplikowaną strukturą plików i procesem kompilacji – po prostu z nich korzystać @wordpress/create-block
aby połączyć wiele projektów blokowych w projekt główny. Następnie można je zarejestrować, po prostu ładując punkt wejścia wtyczki każdego bloku lub migrując wszystkie register_block_type()
/register_block_type_from_metadata()
wywołuje główny punkt wejścia PHP Twojego projektu.
To ustawienie bezpośrednio nadaje się do praktyk monorepo, takich jak te szczegółowo opisane w linku w odpowiedzi @Luismi, a także w tym artykule LogRocket.
Takie podejście można również połączyć z jednym z poniższych rozwiązań, aby skonsolidować poszczególne projekty blokowe ze wspólnym procesem kompilacji i katalogiem wyjściowym. Na papierze wydaje się to całkiem przekonujące, ale nie badałem takiej możliwości.
Rozwiązanie z wieloma konfiguracjami (najbardziej rozsądny bankomat)
Webpack obsługuje pliki konfiguracyjne eksportujące tablicę obiektów konfiguracyjnych, co pozwala na zapewnienie indywidualnej konfiguracji do wykorzystania dla każdego bloku.
Niestety, ponieważ Node.js buforuje i ponownie wykorzystuje import modułów i ponieważ jest to ustawienie domyślne @wordpress/scripts
obiekt konfiguracyjny zawiera różne skonstruowane obiekty i funkcje, użycie nawet rekursywnej kopii obiektu dla każdego bloku może powodować problemy, ponieważ wielokrotne kompilacje Webpacka mogą skutkować ponownym wykorzystaniem instancji wtyczek, które mogą mieć brudny stan z poprzednich kompilacji.
Myślę, że najlepszym sposobem na wdrożenie tego może być utworzenie czegoś w rodzaju „funkcji fabryki konfiguracji”, której można użyć do utworzenia nowego obiektu konfiguracyjnego – po prostu kopiując i wklejając domyślną konfigurację do funkcji.
Jako swego rodzaju hackerską alternatywę usunięcie domyślnej konfiguracji z pamięci podręcznej modułu Node.js powoduje utworzenie zupełnie nowej kopii obiektu za każdym razem require()
’D. Nie jestem jednak pewien, czy jest to dobra praktyka. Ufałbym, że poprzednia metoda będzie bardziej niezawodna i ogólnie akceptowalna. Niemniej jednak, to znacznie ułatwia sprawę:
/**
* `@wordpress/scripts` multi-config multi-block Webpack configuration.
* @see
*/
// Native Depedencies.
const path = require( 'path' );
// Third-Party Dependencies.
const CopyPlugin = require( 'copy-webpack-plugin' );
const default_config_path = require.resolve( '@wordpress/scripts/config/webpack.config.js' );
/**
* Retrieves a new instance of `@wordpress/scripts`' default webpack configuration object.
* @returns WebpackOptions
*/
const getBaseConfig = () => {
// If the default config's already been imported, clear the module from the cache so that Node
// will interpret the module file again and provide a brand new object.
if( require.cache[ default_config_path ] )
delete require.cache[ default_config_path ];
// Import a new instance of the default configuration object.
return require( default_config_path );
};
/**
* @callback buildConfig~callback
* @param {WebpackOptions} config An instance of `@wordpress/scripts`' default configuration object.
* @returns WebpackOptions The modified or replaced configuration object.
*/
/**
* Returns the result of executing a callback function provided with a new default configuration
* instance.
*
* @param {buildConfig~callback} callback
* @returns WebpackOptions The modified or replaced configuration object.
*/
const buildConfig = ( callback ) => callback( getBaseConfig() );
/**
* Extends `@wordpress/scripts`'s default webpack config to build block sources from a common
* `./src/blocks` directory and output built assets to a common `./build/blocks` directory.
*
* @param {string} block_name
* @returns WebpackOptions A configuration object for this block.
*/
const buildBlockConfig = ( block_name ) => buildConfig(
config => (
{ // Copy all properties from the base config into the new config, then override some.
...config,
// Override the block's "index" entry point to be `./src/blocks/{block name}/index.js`.
entry: {
index: path.resolve( process.cwd(), 'src', 'blocks', block_name, 'index.js' ),
},
// This block's built assets should be output to `./build/blocks/{block name}/`.
output: {
...config.output,
path: path.resolve( config.output.path, 'blocks', block_name ),
},
// Add a CopyWebpackPlugin to copy over the `block.json` file.
plugins: [
...config.plugins,
new CopyPlugin(
{
patterns: [
{ from: `src/blocks/${block_name}/block.json` },
],
}
),
]
}
)
);
module.exports = [
buildBlockConfig( 'foo' ),
buildBlockConfig( 'bar' ),
// Setup a configuration to build `./src/frontend/accordion.js` to `./build/frontend/`
buildConfig(
config => (
{
...config,
entry: {
accordion: path.resolve( process.cwd(), 'src', 'frontend', 'accordion.js' ),
},
output: {
...config.output,
path: path.resolve( config.output.path, 'frontend' ),
},
}
)
)
];
Rozwiązanie nazw wpisów oparte na ścieżce
Rozwiązanie to polega na zmianie
@wordpress/scripts
które nie będą dostępne aż do następnej wersji (wersja pakietu > 16.1.3). Aby z niego teraz skorzystać, musisz zainstalować pakiet z GitHuba. Zbliżająca się aktualizacja wp-scripts do pakietu Webpack 5 powinna również ułatwić to podejście.
Moim zdaniem najwygodniejszym rozwiązaniem jest po prostu użycie częściowych ścieżek jako nazw punktów wejścia:
/**
* `@wordpress/scripts` path-based name multi-block Webpack configuration.
* @see
*/
// Native Depedencies.
const path = require( 'path' );
// Third-Party Dependencies.
const CopyPlugin = require( 'copy-webpack-plugin' );
const config = require( '@wordpress/scripts/config/webpack.config.js' );
/**
* Resolve a series of path parts relative to `./src`.
* @param string[] path_parts An array of path parts.
* @returns string A normalized path, relative to `./src`.
**/
const resolveSource = ( ...path_parts ) => path.resolve( process.cwd(), 'src', ...path_parts );
/**
* Resolve a block name to the path to it's main `index.js` entry-point.
* @param string name The name of the block.
* @returns string A normalized path to the block's entry-point file.
**/
const resolveBlockEntry = ( name ) => resolveSource( 'blocks', name, 'index.js' );
config.entry = {
'blocks/foo/index': resolveBlockEntry( 'foo' ),
'blocks/bar/index': resolveBlockEntry( 'bar' ),
'frontend/accordion': resolveSource( 'frontend', 'accordion.js' ),
};
// Add a CopyPlugin to copy over block.json files.
config.plugins.push(
new CopyPlugin(
{
patterns: [
{
context: 'src',
from: `blocks/*/block.json`
},
],
}
)
);
module.exports = config;
Jest to swego rodzaju wygoda, ponieważ jest prosta, zwięzła i łatwa w utrzymaniu… z pewnym zastrzeżeniem.
Ze względu na sposób @wordpress/scripts
obsługuje nazwy plików CSS/SCSS „styl” i „styl.module”, aby obejść ograniczenia pakietu Webpack 4, musimy zmodyfikować sposób nazywania tych plików, aby mieć pewność, że zasoby „stylu” znajdą się w tym samym katalogu, podobnie jak reszta zbudowanych zasobów. To brzydkie i Nie przetestowałem dokładnie możliwych przypadków brzegowych (w szczególności może robić dziwne rzeczy, jeśli plik „stylu” tworzy wiele fragmentów) – ale przy odrobinie szczęścia nie będzie to konieczne w pakiecie Webpack 5:
config.optimization.splitChunks.cacheGroups.style.name = ( module, chunks, group_key ) => {
const delimeter = config.optimization.splitChunks.cacheGroups.style.automaticNameDelimiter;
return chunks[0].name.replace(
/(\/?)([^/]+?)$/,
`$1${group_key}${delimeter}$2`
);
};
Płaskie rozwiązanie wyjściowe
Bardzo minimalna konfiguracja może ułatwić kompilację zasobów do bardzo brzydkiej struktury wyjściowej w postaci płaskiego pliku:
/**
* `@wordpress/scripts` flat output multi-block Webpack configuration.
* @see
*/
// Native Depedencies.
const path = require( 'path' );
// Third-Party Dependencies.
const CopyPlugin = require( 'copy-webpack-plugin' );
const config = require( '@wordpress/scripts/config/webpack.config.js' );
config.entry = {
'foo-block': path.resolve( process.cwd(), 'src', 'blocks', 'foo', 'index.js' ),
'bar-block': path.resolve( process.cwd(), 'src', 'blocks', 'bar', 'index.js' ),
'accordion': path.resolve( process.cwd(), 'src', 'frontend', 'accordion.js' ),
};
config.plugins.push(
new CopyPlugin(
{
patterns: [
{
context: 'src/blocks',
from: '*/block.json',
to: ( { absoluteFilename } ) => `block-${absoluteFilename.match( /[\\/]([^\\/]+)[\\/]block.json/ )[1]}.json`,
},
],
}
)
);
module.exports = config;
Na papierze można ustawić funkcję
config.output.filename
aby ta prosta konfiguracja ponownie wygenerowała zagnieżdżoną strukturę wyjściową (zastępując-
w nazwie punktu wejścia za pomocą/
lub podobny), ale to również nie jest obecnie możliwe w wynikuFixWebpackStylePlugin
wdrożenie.