Giter Site home page Giter Site logo

cakephp-vite's Introduction

ViteHelper plugin for CakePHP 5

The plugin provides a Helper Class for CakePHP to facilitate the use of Vite JS.

When running the Vite dev server, the Helper provides the right script tags for hot module replacement and page reloading.

In production mode, the Helper loads the bundled files. @vitejs/plugin-legacy is supported, which will insert nomodule-tags for older browsers, e.g. older iOS devices, which do not support js modules.

This readme is for version 1.x. If you are migrating from 0.x and something is unclear, read the Migration guide under /docs. Feel free to open an issue if you run into problems.

Installation

You can install this plugin into your CakePHP application using composer.

CakePHP Version Map

CakePHP version Plugin Version Branch min. PHP Version
^3.10 / cake3 ^7.4
^4.2 0.x master ^7.4
^4.2 1.x master ^8.0
^5.0 2.x cake5 ^8.1

The recommended way to install the plugin is:

composer require passchn/cakephp-vite

Load the plugin in your Application.php:

bin/cake plugin load ViteHelper

Load the Helper in your AppView.php:

$this->loadHelper('ViteHelper.ViteScripts');

Usage

In your php-layout, include this in your html head:

<?= $this->fetch('css') ?>

Just before the closing </body> tag, insert this line:

<?= $this->fetch('script') ?>

These are the default view blocks in CakePHP. Lern more about view blocks in the Book.

In your php-template or in layout you can import javascript files with:

<?php $this->ViteScripts->script($options) ?>

… or by using this shortcut for a single entrypoint:

<?php $this->ViteScripts->script('webroot_src/main.ts') ?>

If you imported CSS files inside your JavaScript files, this method automatically appends your css tags to the css view block.

If you don't have any css-entries defined in your vite-config, you can skip the ::css() method call.

In your php-template you can import css files with:

<?php $this->ViteScripts->css($options) ?>

… or by using this shortcut for a single entrypoint:

<?php $this->ViteScripts->css('webroot_src/style.css') ?>

Configuration

The plugin comes with some default configuration. You may need to change it depending on your setup. Or you might not need any config at all.

You can override some of these config settings through the $options of the helper methods. Or you can pass your own instance of ViteHelperConfig to a helper method as a second parameter.

'ViteHelper' => [
    'build' => [
        'outDirectory' => false, // output directory of build assets. string (e.g. 'dist') or false.
        'manifest' => WWW_ROOT . 'manifest.json', // absolute path to manifest
    ],
    'development' => [
        'scriptEntries' => ['someFolder/myScriptEntry.ts'], // relative to project root
        'styleEntries' =>  ['someFolder/myStyleEntry.scss'], // relative to project root. Unnecessary when using css-in-js.
        'hostNeedles' => ['.test', '.local'], // to check if the app is running locally
        'url' => 'http://localhost:3000', // url of the vite dev server
    ],
    'forceProductionMode' => false, // or true to always serve build assets
    'plugin' => false, // or string 'MyPlugin' to serve plugin build assets
    'productionHint' => 'vprod', // can be a true-ish cookie or url-param to serve build assets without changing the forceProductionMode config
    'viewBlocks' => [
        'css' => 'css', // name of the css view block
        'script' => 'script', // name of the script view block
    ],
],

You can override the defaults in your app.php, app_local.php, or app_vite.php.

See the plugin's app_vite.php for reference.

Example:

return [
    'ViteHelper' => [
        'forceProductionMode' => 1,
        'development' => [
            'hostNeedles' => ['.dev'], // if you don't use one of the defaults
            'url' => 'https://192.168.0.88:3000',
        ],
    ],
];

Helper method usage with options

You can pass an $options array to override config or to completely skip the necessity to have a ViteHelper config.

The options are mostly the same for ::script() and ::css().

Example

$this->ViteScripts->script([

    // this would append both the scripts and the css to a block named 'myCustomBlock'
    // don't forget to use the block through $this->fetch('myCustomBlock')
    'block' => 'myCustomBlock',
    'cssBlock' => 'myCustomBlock', // for ::script() only – if you use css imports inside js.

    // files that are entry files during development and that should be served during production
    'files' => [
        'webroot_src/main.ts',
    ],

    // "devEntries" is like "files". If you set "files", it will override both "devEntries" and "prodFilters"
    'devEntries' => ['webroot_src/main.ts']

    // "prodFilter" filters the entry files. Useful for code-splitting if you don't use dynamic imports
    'prodFilter' => 'webroot_src/main.ts' // as string if there's only one option
    'prodFilter' => 'main.ts' // also works - only looks for parts of the string
    'prodFilter' => ['main.ts'] // as array - same as above with multiple files
    'prodFilter' => function (ManifestRecord $record) { /* do something with the record and return true or false */ }
]);

Note: You need to set devEntries when running the dev server. They have to either be set in the config or through the helper method. In contrast, you only need files or prodFilter if you are interested in php-side code-splitting and don't use dynamic imports in js.

It depends on your project and use case how you define entries. If you don't use prodFilter or files, the plugin will serve all your entry files which might just be the case you want. So don't overconfigure it ;)

Opt out of global config + Plugin development

You can use the helper methods with multiple configurations through the $config argument.

New in version 2.3: You can pass a config key instead of a config instance to the helper. The default config key is ViteHelper.

This might be useful when using plugin scripts or when developing plugins:

<?php $this->ViteScripts->pluginScript('MyPlugin', devMode: true, config: 'MyPlugin.ViteConfig'); ?>

The example above uses a convenience method to load plugin scripts for MyPlugin. DevMode is enabled and the helper will use a CakePHP config under the key MyPlugin.ViteConfig. In this way, you can scope your App's and your plugin's config.

It is assumed that the manifest.json is available directly in your plugin's webroot. If this is not the case, you should define the absolute path throuh the build.manifest config option.

Vite JS bundler / Dev server

Install Vite e.g. via yarn:

yarn create vite

It is recommended to add the legacy plugin:

yarn add -D @vitejs/plugin-legacy

See Scaffolding Your First Vite Project on vitejs.dev for more information.

Configuration

After installing, you will need to refactor the files a bit to make sense of it in a php project. The default config of this plugin assumes that you put your js, ts, scss etc. in /webroot_src.

The build files will end up in /webroot/assets by default. Your vite.config.js or *.ts file for vite stays in the project root.

Wanted: Examples for vite/plugin configs and directory structures. Feel free to contribute with a PR to show how your project uses this plugin.

Recommended configuration:

See the example vite.config.ts content here. Note that the config changes when upgrading to vite version 2.9.0 or higher.

A difference to other dev servers, e.g. webpack or gulp is that you won't access your local site via the port where Vite is serving. This does not work with php.

Therefore, the server.hmr config will enable you to use hot module replacement via the websocket protocol.

The build options define where the bundled js and css will end up. These need to match plugin config.

More about configuring Vite can be found at vitejs.dev/config.

Contributions

You can contribute to this plugin via pull requests. If you run into an error, you can open an issue.

cakephp-vite's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

cakephp-vite's Issues

build.emptyOutDir not working with modules/dynamic imports

Vite config:

build: {
        emptyOutDir: true,
        outDir: './webroot/dist',
        manifest: true,
        rollupOptions: {
            input: {
                main: './webroot_src/main.ts',
            },
        },
    },

Generated html:
out-dir-bug

The green scripts are inserted by the plugin, but the red above come from vite. Vite expects the outDir to be the root which is accessible by apache etc.

So there is no /dist prefix and dynamic modules cannot be loaded.

A solution would be to set outDir to ./webroot. But then, vite will remove all files in webroot if emptyOutDir is true.

Does anyone have an idea how to fix that? If not, emptyOutDir would not work if you have static assets in webroot that don't come from vite.

You would have to delete the /assets folder before building manually here and there (not a big deal though).

future/v1: 404's trying to read files from manifest

In branch future/v1 I get 404's trying to read files from manifest with 'forceProductionMode' => true,. This is my vite.config.js build object:

build: {
      manifest: true,
      rollupOptions: {
        input: {
          // Styles
          styles: 'resources/styles/main.css',
          critical: 'resources/styles/critical.css',
          storybook: 'resources/styles/storybook.css',

          // Scripts
          main: 'resources/scripts/main.ts',
        },
        output: {
          entryFileNames: 'assets/[name]-[hash].js',
          chunkFileNames: 'assets/[name]-[hash].js',
          assetFileNames: 'assets/[name]-[hash].[ext]',
        },
      },
      outDir: '../webroot/dist',
      emptyOutDir: true,
    },

Which results in this build structure:
Schermafbeelding_20230121_164448
As you can see the manifest.json gets put inside the dist folder by vite. This is the generated manifest.json:

{
  "styles/main.css": {
    "file": "assets/main-e7a50703.css",
    "src": "styles/main.css",
    "isEntry": true
  },
  "styles/storybook.css": {
    "file": "assets/storybook-3b78efcc.css",
    "src": "styles/storybook.css",
    "isEntry": true
  },
  "styles/critical.css": {
    "file": "assets/critical-a9e7c1bb.css",
    "src": "styles/critical.css",
    "isEntry": true
  },
  "scripts/components/t-button/t-button.ce.vue": {
    "file": "assets/t-button.ce-9bcdf683.js",
    "src": "scripts/components/t-button/t-button.ce.vue",
    "isDynamicEntry": true,
    "imports": [
      "scripts/main.ts"
    ]
  },
  "scripts/main.ts": {
    "file": "assets/main-a077a8fb.js",
    "src": "scripts/main.ts",
    "isEntry": true,
    "dynamicImports": [
      "scripts/components/t-button/t-button.ce.vue"
    ]
  }
}

Because it says return '/' in ManifestRecord.php lines 94 and 112 it will return http://project.test/assets/main-a077a8fb.js but I want it to return http://project.test/dist/assets/main-a077a8fb.js.

Setting build.outDirectory to dist does nothing because outDir isn't used anywhere in ManifestRecord.php. I would expect build.outDirectory to prefix all url's read from the manifest, can you fix this?

Please delete @vite/client

It seems the newest version of Vite loads the client automatically, can you confirm?
If confirmed, please delete:

        $this->Html->script(
            $config->read('development.url', ConfigDefaults::DEVELOPMENT_URL)
            . '/@vite/client',
            [
                'type' => 'module',
                'block' => $options['cssBlock'],
            ]
        );

in ViteScriptsHelper.php

On Windows environments path to assets are broken as they contain incorrect slash

Hello, first at all - thank you for your time you used to develop this plugin. Unfortunately today I spotted one problem when I was trying to use it "production" mode.

Generally on Windows environments DS is equal to "\" which should not be used in URLs which are produced by e.g. getLinkFromOutDirectory method.

Like mentioned above, DS is used in e.g. getLinkFromOutDirectory method from ManifestRecord class:

return $outDirectory . DS . $assetLink;

to build path to assets like js or css files. This, on Windows, creates following, incorrect URLs like http://test.localhost/js/%5Cassets/main-5de7cc61.js which of course does not load.

Here is my cakephp-vite plugin configuration:

'ViteHelper' => [
    'development' => [
        'scriptEntries' => ['webroot_src/main.js'],
    ],
]

Here is vite.confg.js I am using:

import {defineConfig} from 'vite'

export default defineConfig({
    build: {
        emptyOutDir: false,
        outDir: './webroot',
        manifest: true,
        rollupOptions: {
            input: './webroot_src/main.js',
        },
    },
    server: {
        port: 3000,
        strictPort: true,
        hmr: {
            protocol: 'ws',
            host: 'localhost',
        },
    },
});

CORS-Issues with dev-server

Known issue: The browser gets a http 426 response ("Upgrade required") and there are CORS-warnings logged in the Console.
The problem occurs from vite version 2.9.0 onwards.

Quickfix: Lock vite to 2.8.6.

See vitejs/vite#8602

CakePHP 5.0 support

Can't seem to add this plugin to a Cake 5 app, showing incompatibility between 5.0 and 5.x-dev versions. Any help?

The script method should accept string as single parameter

Just a small QoL idea.

This:

$this->ViteScripts->script([
    // files that are entry files during development and that should be served during production
    'files' => [
        'webroot_src/main.ts',
    ],
]);

should be equal with this:

$this->ViteScripts->script('webroot_src/main.ts');

Yay or Nay?

.js suffix added to css entries

In my vite.config.js I have styles: './resources/styles/main.css' as an input entry. When I build, it becomes "styles/main.css": { "file": "assets/main-186c74ee.css", "src": "styles/main.css", "isEntry": true }, in the manifest. Yet when I load my page and the manifest gets read, I get a 404 on it because it tries to get http://localhost/assets/main-186c74ee.css.js. Why does it add the .js suffix? If this is a bug, can you fix it?

I'm using PHP 8.0.24 and cakePHP 4.4.10

The entry file "styles/main.css" does not exist even though styleEntries is set correct

See title. I have:

        'development' => [
            'scriptEntries' => ['scripts/main.ts'],
            'styleEntries' => ['styles/main.css'],
            'url' => 'http://localhost:5173',
        ],

It can find the main.ts but not the main.css, also I think you should be able to not set styleEntries at all and not have it give the error There are no valid entry points for the dev server. Be sure to set the ViteHelper.development.scriptEntries config. or if you decide to keep it change the error for styles to Be sure to set the ViteHelper.development.styleEntries config.

Enable WSS (Secure Websocket) usage

Development under Safari is currently not possible, a [Warning] [blocked] The page at https://my-domain.local was not allowed to run insecure content from http://localhost:3000/webroot_src/main.ts. etc. warning will be logged.

When switching the vite config to wss like so:

    server: {
        hmr: {
            protocol: 'wss',
            host: 'localhost',
            port: 3000,
        },
    },

... and changing http://localhost: to https://localhost: in the ViteScriptsHelper, it still won't work due to SSL errors/certificate mismatches.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.