Skip to content

Commit

Permalink
Build the API Explorer with Vite
Browse files Browse the repository at this point in the history
The main goal of this contribution is to make possible to bundle app
with stylesheets with Vite.

Vite expects the main entrypoint to define the stylesheets to load so it
excludes them from the manifets files as root entries. This is fine for
most cases where we can tell the app to load the stylesheets.
There is a few cases in Tuleap where this is not possible (mostly global
themes/"framework") which we will need additional work to be built with
Vite. There is however no need at this point but it could be handled in
a similar way than what vite_ruby does with a custom manifest plugin [0].

The warning that is now visible when buiding the API Explorer is due to
a IE7 workaround and will be removed the next time we update swagger-ui [1]
(and they rebuild their distributed files).

No functionnal changes expected.

Part of request #23415: Allow to bundle app with Vite

[0] https://github.com/ElMassimo/vite_ruby/blob/vite-plugin-ruby%403.0.3/vite-plugin-ruby/src/manifest.ts
[1] swagger-api/swagger-ui#7526

Change-Id: I5e6b13756d3c4fbff99439b470fdb795a8ab7b72
  • Loading branch information
LeSuisse committed Dec 21, 2021
1 parent 53ce44b commit 20f24ab
Show file tree
Hide file tree
Showing 28 changed files with 453 additions and 171 deletions.
16 changes: 4 additions & 12 deletions plugins/api_explorer/include/ExplorerController.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
use HTTPRequest;
use TemplateRenderer;
use Tuleap\Layout\BaseLayout;
use Tuleap\Layout\CssAssetWithoutVariantDeclinaisons;
use Tuleap\Layout\IncludeAssets;
use Tuleap\Layout\IncludeViteAssets;
use Tuleap\Layout\JavascriptViteAsset;
use Tuleap\Request\DispatchableWithBurningParrot;
use Tuleap\Request\DispatchableWithRequestNoAuthz;

Expand All @@ -36,25 +36,17 @@ final class ExplorerController implements DispatchableWithRequestNoAuthz, Dispat
* @var TemplateRenderer
*/
private $renderer;
/**
* @var IncludeAssets
*/
private $assets;

public function __construct(TemplateRenderer $renderer, IncludeAssets $assets)
public function __construct(TemplateRenderer $renderer, private IncludeViteAssets $assets)
{
$this->renderer = $renderer;
$this->assets = $assets;
}

public function process(HTTPRequest $request, BaseLayout $layout, array $variables): void
{
\Tuleap\Project\ServiceInstrumentation::increment(\api_explorerPlugin::SERVICE_NAME_INSTRUMENTATION);

$layout->includeFooterJavascriptFile($this->assets->getFileURL('api-explorer.js'));
$layout->addCssAsset(
new CssAssetWithoutVariantDeclinaisons($this->assets, 'style-api-explorer')
);
$layout->addJavascriptAsset(new JavascriptViteAsset($this->assets, 'scripts/index.tsx'));

$layout->header(['title' => dgettext('tuleap-api_explorer', 'API Explorer'), 'main_classes' => ['tlp-framed']]);
$this->renderer->renderToPage('explorer', []);
Expand Down
2 changes: 1 addition & 1 deletion plugins/api_explorer/include/api_explorerPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public function routeGet(): \Tuleap\APIExplorer\ExplorerController
{
return new \Tuleap\APIExplorer\ExplorerController(
TemplateRendererFactory::build()->getRenderer(__DIR__ . '/../templates/'),
new \Tuleap\Layout\IncludeAssets(
new \Tuleap\Layout\IncludeViteAssets(
__DIR__ . '/../../../src/www/assets/api-explorer',
'/assets/api-explorer'
)
Expand Down
4 changes: 2 additions & 2 deletions plugins/api_explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"license": "GPL-2.0-or-later",
"private": true,
"scripts": {
"build": "webpack --config webpack.prod.js",
"watch": "webpack --config webpack.dev.js --watch"
"build": "vite build",
"watch": "vite build --watch"
},
"dependencies": {
"react": "^17.0.2",
Expand Down
1 change: 1 addition & 0 deletions plugins/api_explorer/scripts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import SwaggerUI from "swagger-ui";
import React from "react"
import "../themes/style.scss";

interface TuleapLayoutProps {
errSelectors: {
Expand Down
9 changes: 3 additions & 6 deletions plugins/api_explorer/tests/unit/ExplorerControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
use TemplateRendererFactory;
use Tuleap\Layout\BaseLayout;
use Tuleap\Layout\IncludeAssets;
use Tuleap\Layout\IncludeViteAssets;
use Tuleap\Templating\TemplateCache;

final class ExplorerControllerTest extends \Tuleap\Test\PHPUnit\TestCase
Expand All @@ -34,7 +34,7 @@ final class ExplorerControllerTest extends \Tuleap\Test\PHPUnit\TestCase

public function testCanProcessARequest(): void
{
$include_assets = \Mockery::mock(IncludeAssets::class);
$include_assets = new IncludeViteAssets('/', '/');

$template_cache = \Mockery::mock(TemplateCache::class);
$template_cache->shouldReceive('getPath')->andReturnNull();
Expand All @@ -45,11 +45,8 @@ public function testCanProcessARequest(): void
$include_assets
);

$include_assets->shouldReceive('getFileURL')->andReturn('file_url');

$layout = \Mockery::mock(BaseLayout::class);
$layout->shouldReceive('includeFooterJavascriptFile')->atLeast()->once();
$layout->shouldReceive('addCssAsset')->atLeast()->once();
$layout->shouldReceive('addJavascriptAsset')->atLeast()->once();
$layout->shouldReceive('header')->once();
$layout->shouldReceive('footer')->once();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) Enalean, 2020-Present. All Rights Reserved.
/**
* Copyright (c) Enalean, 2021-Present. All Rights Reserved.
*
* This file is a part of Tuleap.
*
Expand All @@ -17,7 +17,19 @@
* along with Tuleap. If not, see <http://www.gnu.org/licenses/>.
*/

const common = require("./webpack.common.js");
const webpack_configurator = require("../../tools/utils/scripts/webpack-configurator.js");
import { defineAppConfig } from "../../tools/utils/scripts/vite-configurator";
import * as path from "path";

module.exports = webpack_configurator.extendProdConfiguration(common);
export default defineAppConfig(
"api-explorer",
{
build: {
rollupOptions: {
input: {
"api-explorer": path.resolve(__dirname, "scripts/index.tsx"),
},
},
},
},
{ typescript: true }
);
47 changes: 0 additions & 47 deletions plugins/api_explorer/webpack.common.js

This file was deleted.

12 changes: 8 additions & 4 deletions src/common/layout/BaseLayout.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ abstract class BaseLayout extends Response
*/
protected $css_assets;
/**
* @var JavascriptAsset[]
* @var JavascriptAssetGeneric[]
*/
protected $javascript_assets = [];
protected array $javascript_assets = [];

/**
* @var string
Expand All @@ -122,12 +122,13 @@ public function __construct($root)
}

abstract public function header(array $params);
abstract protected function hasHeaderBeenWritten(): bool;
abstract public function footer(array $params);
abstract public function displayStaticWidget(Widget_Static $widget);
abstract public function includeCalendarScripts();
abstract protected function getUser();

public function addCssAsset(CssAsset $asset)
public function addCssAsset(CssAssetGeneric $asset): void
{
$this->css_assets = $this->css_assets->merge(new CssAssetCollection([$asset]));
}
Expand Down Expand Up @@ -186,8 +187,11 @@ public function includeFooterJavascriptFile($file)
$this->javascript_in_footer[] = ['file' => $file];
}

public function addJavascriptAsset(JavascriptAsset $asset): void
public function addJavascriptAsset(JavascriptAssetGeneric $asset): void
{
if ($this->hasHeaderBeenWritten() && ($asset->getType() === 'module' || $asset->getAssociatedCSSAssets()->getDeduplicatedAssets() !== [])) {
throw new \RuntimeException('JavaScript module asset or with associated CSS assets must be added before the page header is written');
}
$this->javascript_assets[] = $asset;
}

Expand Down
4 changes: 2 additions & 2 deletions src/common/layout/CssAsset.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

namespace Tuleap\Layout;

class CssAsset
class CssAsset implements CssAssetGeneric
{
/**
* @var IncludeAssets
Expand All @@ -47,7 +47,7 @@ public function getFileURL(ThemeVariation $variant): string
return $this->include_assets->getFileURL($this->name . $variant->getFileColorSuffix() . '.css');
}

public function getPath(): string
public function getIdentifier(): string
{
return $this->include_assets->getPath($this->name);
}
Expand Down
19 changes: 12 additions & 7 deletions src/common/layout/CssAssetCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@

class CssAssetCollection
{
/** @var CssAsset[] */
private $css_assets = [];
/** @var CssAssetGeneric[] */
private array $css_assets = [];

/**
* @param CssAsset[] $css_assets
* @param CssAssetGeneric[] $css_assets
*/
public function __construct(array $css_assets)
{
Expand All @@ -35,10 +35,15 @@ public function __construct(array $css_assets)
}
}

private function addWithoutDuplicate(CssAsset $asset): void
public static function empty(): self
{
if (! isset($this->css_assets[$asset->getPath()])) {
$this->css_assets[$asset->getPath()] = $asset;
return new self([]);
}

private function addWithoutDuplicate(CssAssetGeneric $asset): void
{
if (! isset($this->css_assets[$asset->getIdentifier()])) {
$this->css_assets[$asset->getIdentifier()] = $asset;
}
}

Expand All @@ -49,7 +54,7 @@ public function merge(CssAssetCollection $collection): CssAssetCollection
}

/**
* @return CssAsset[]
* @return CssAssetGeneric[]
*/
public function getDeduplicatedAssets(): array
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) Enalean, 2020-Present. All Rights Reserved.
<?php
/**
* Copyright (c) Enalean, 2021-Present. All Rights Reserved.
*
* This file is a part of Tuleap.
*
Expand All @@ -15,9 +16,15 @@
*
* You should have received a copy of the GNU General Public License
* along with Tuleap. If not, see <http://www.gnu.org/licenses/>.
*
*/

const common = require("./webpack.common.js");
const webpack_configurator = require("../../tools/utils/scripts/webpack-configurator.js");
declare(strict_types=1);

namespace Tuleap\Layout;

module.exports = webpack_configurator.extendDevConfiguration(common);
interface CssAssetGeneric
{
public function getFileURL(ThemeVariation $variant): string;
public function getIdentifier(): string;
}
51 changes: 51 additions & 0 deletions src/common/layout/CssViteAsset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php
/**
* Copyright (c) Enalean, 2021-Present. All Rights Reserved.
*
* This file is a part of Tuleap.
*
* Tuleap is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tuleap is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tuleap. If not, see <http://www.gnu.org/licenses/>.
*
*/

declare(strict_types=1);

namespace Tuleap\Layout;

final class CssViteAsset implements CssAssetGeneric
{
private function __construct(private string $file_url)
{
}

public static function buildCollectionFromMainFileName(IncludeViteAssets $include_assets, string $file_name): CssAssetCollection
{
return new CssAssetCollection(
array_map(
static fn (string $file_url) => new self($file_url),
$include_assets->getStylesheetsURLs($file_name)
)
);
}

public function getFileURL(ThemeVariation $variant): string
{
return $this->file_url;
}

public function getIdentifier(): string
{
return basename($this->file_url);
}
}
20 changes: 20 additions & 0 deletions src/common/layout/IncludeViteAssets.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,26 @@ public function getFileURL(string $file_name): string
return $this->getBaseURLWithTrailingSlash() . $this->getHashedName($file_name);
}

/**
* @return string[]
*/
public function getStylesheetsURLs(string $file_name): array
{
$stylesheets = [];

if ($this->assets === null) {
$this->assets = $this->loadFromManifest();
}
$stylesheet_filename_hashes = $this->assets[$file_name]['css'] ?? [];
$base_url = $this->getBaseURLWithTrailingSlash();

foreach ($stylesheet_filename_hashes as $stylesheet_filename_hash) {
$stylesheets[] = $base_url . $stylesheet_filename_hash;
}

return $stylesheets;
}

private function getBaseURLWithTrailingSlash(): string
{
return rtrim($this->base_url, '/') . '/';
Expand Down
Loading

0 comments on commit 20f24ab

Please sign in to comment.