WordPress

Czy można używać @wordpress/create-block z wieloma blokami?

  • 8 czerwca, 2021
  • 11 min read
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 polu block.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, webpackI eslintaby 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.jsi 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:

Warto przeczytać!  migracja — Jak dodać wyróżniony obraz na WXR z zewnętrznej konwersji

Struktura projektu multiblokowego @bosco

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.

Warto przeczytać!  Jak dodać niestandardowe powiadomienia administracyjne w WordPress

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.

Warto przeczytać!  Wybierz listę rozwijaną ze zdarzeniem onChange i funkcją wyszukiwania — za pomocą Selectize

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;

płaska moc wyjściowa

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 wyniku FixWebpackStylePluginwdrożenie.


Źródło