From 1a29ef2ccb14eae2fd19faeb122bb4ab671400fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zieli=C5=84ski?= Date: Tue, 30 May 2023 17:18:08 +0200 Subject: [PATCH] Remove interactive-code-block from the main repository. See #473. --- .../interactive-code-block/.eslintrc.json | 18 - packages/interactive-code-block/README.md | 11 - packages/interactive-code-block/package.json | 6 - packages/interactive-code-block/project.json | 55 --- .../interactive-code-block/public/block.php | 115 ------ .../public/interactive-code-block.php | 115 ------ .../public/playground-sandbox.html | 70 ---- ...est-api-lcs-execution-scripts-endpoint.php | 218 ------------ .../rest-api-lcs-libraries-endpoint.php | 233 ------------- packages/interactive-code-block/src/index.ts | 0 .../src/lib/block/base64-utils.ts | 7 - .../src/lib/block/block.json | 36 -- .../src/lib/block/edit.tsx | 229 ------------ .../src/lib/block/editor.css | 27 -- .../src/lib/block/editor.tsx | 36 -- .../src/lib/block/php-wasm.php | 25 -- .../src/lib/block/view-async.tsx | 54 --- .../src/lib/block/view.css | 51 --- .../src/lib/block/view.ts | 3 - .../without-gutenberg-keyboard-shortcuts.tsx | 84 ----- .../src/lib/components/code-mirror/index.tsx | 190 ---------- .../src/lib/components/code-output/index.tsx | 268 -------------- .../src/lib/components/code-output/loader.tsx | 17 - .../components/code-output/style.module.css | 72 ---- .../src/lib/components/code-runner/index.tsx | 58 --- .../default-bootstrap-script.php | 3 - .../interactive-code-snippet/index.tsx | 102 ------ .../interactive-code-snippet/style.module.css | 31 -- .../components/libraries-control/index.tsx | 67 ---- .../src/lib/components/php-runner/index.tsx | 140 -------- .../lib/components/php-runner/php-loader.ts | 49 --- .../components/playground-runner/index.tsx | 135 ------- .../src/lib/components/progress-bar/index.tsx | 16 - .../components/progress-bar/style.module.css | 35 -- .../lib/components/toolbar-dropdown/index.tsx | 94 ----- .../hooks/use-has-transition-class-name.tsx | 28 -- .../src/lib/hooks/use-libraries.tsx | 143 -------- .../src/lib/hooks/use-on-escape-key.tsx | 16 - .../src/lib/hooks/use-promise.ts | 9 - .../src/lib/pages/entities.tsx | 26 -- .../src/lib/pages/execution-scripts-forms.tsx | 329 ------------------ .../src/lib/pages/execution-scripts-list.tsx | 160 --------- .../src/lib/pages/execution-scripts-page.tsx | 21 -- .../src/lib/pages/libraries-list.tsx | 192 ---------- .../src/lib/pages/libraries-page.tsx | 21 -- .../src/lib/pages/notifications.tsx | 21 -- .../src/lib/pages/upload-overlay.tsx | 31 -- .../src/lib/php-worker.ts | 40 --- .../interactive-code-block/src/lib/types.ts | 67 ---- .../interactive-code-block/src/lib/utils.ts | 5 - packages/interactive-code-block/tsconfig.json | 24 -- .../interactive-code-block/tsconfig.lib.json | 18 - .../interactive-code-block/tsconfig.spec.json | 19 - .../interactive-code-block/vite.config.ts | 264 -------------- tsconfig.base.json | 3 - 55 files changed, 4107 deletions(-) delete mode 100644 packages/interactive-code-block/.eslintrc.json delete mode 100644 packages/interactive-code-block/README.md delete mode 100644 packages/interactive-code-block/package.json delete mode 100644 packages/interactive-code-block/project.json delete mode 100644 packages/interactive-code-block/public/block.php delete mode 100644 packages/interactive-code-block/public/interactive-code-block.php delete mode 100644 packages/interactive-code-block/public/playground-sandbox.html delete mode 100644 packages/interactive-code-block/public/rest-api-lcs-execution-scripts-endpoint.php delete mode 100644 packages/interactive-code-block/public/rest-api-lcs-libraries-endpoint.php delete mode 100644 packages/interactive-code-block/src/index.ts delete mode 100644 packages/interactive-code-block/src/lib/block/base64-utils.ts delete mode 100644 packages/interactive-code-block/src/lib/block/block.json delete mode 100644 packages/interactive-code-block/src/lib/block/edit.tsx delete mode 100644 packages/interactive-code-block/src/lib/block/editor.css delete mode 100644 packages/interactive-code-block/src/lib/block/editor.tsx delete mode 100644 packages/interactive-code-block/src/lib/block/php-wasm.php delete mode 100644 packages/interactive-code-block/src/lib/block/view-async.tsx delete mode 100644 packages/interactive-code-block/src/lib/block/view.css delete mode 100644 packages/interactive-code-block/src/lib/block/view.ts delete mode 100644 packages/interactive-code-block/src/lib/block/without-gutenberg-keyboard-shortcuts.tsx delete mode 100644 packages/interactive-code-block/src/lib/components/code-mirror/index.tsx delete mode 100644 packages/interactive-code-block/src/lib/components/code-output/index.tsx delete mode 100644 packages/interactive-code-block/src/lib/components/code-output/loader.tsx delete mode 100644 packages/interactive-code-block/src/lib/components/code-output/style.module.css delete mode 100644 packages/interactive-code-block/src/lib/components/code-runner/index.tsx delete mode 100644 packages/interactive-code-block/src/lib/components/interactive-code-snippet/default-bootstrap-script.php delete mode 100644 packages/interactive-code-block/src/lib/components/interactive-code-snippet/index.tsx delete mode 100644 packages/interactive-code-block/src/lib/components/interactive-code-snippet/style.module.css delete mode 100644 packages/interactive-code-block/src/lib/components/libraries-control/index.tsx delete mode 100644 packages/interactive-code-block/src/lib/components/php-runner/index.tsx delete mode 100644 packages/interactive-code-block/src/lib/components/php-runner/php-loader.ts delete mode 100644 packages/interactive-code-block/src/lib/components/playground-runner/index.tsx delete mode 100644 packages/interactive-code-block/src/lib/components/progress-bar/index.tsx delete mode 100644 packages/interactive-code-block/src/lib/components/progress-bar/style.module.css delete mode 100644 packages/interactive-code-block/src/lib/components/toolbar-dropdown/index.tsx delete mode 100644 packages/interactive-code-block/src/lib/hooks/use-has-transition-class-name.tsx delete mode 100644 packages/interactive-code-block/src/lib/hooks/use-libraries.tsx delete mode 100644 packages/interactive-code-block/src/lib/hooks/use-on-escape-key.tsx delete mode 100644 packages/interactive-code-block/src/lib/hooks/use-promise.ts delete mode 100644 packages/interactive-code-block/src/lib/pages/entities.tsx delete mode 100644 packages/interactive-code-block/src/lib/pages/execution-scripts-forms.tsx delete mode 100644 packages/interactive-code-block/src/lib/pages/execution-scripts-list.tsx delete mode 100644 packages/interactive-code-block/src/lib/pages/execution-scripts-page.tsx delete mode 100644 packages/interactive-code-block/src/lib/pages/libraries-list.tsx delete mode 100644 packages/interactive-code-block/src/lib/pages/libraries-page.tsx delete mode 100644 packages/interactive-code-block/src/lib/pages/notifications.tsx delete mode 100644 packages/interactive-code-block/src/lib/pages/upload-overlay.tsx delete mode 100644 packages/interactive-code-block/src/lib/php-worker.ts delete mode 100644 packages/interactive-code-block/src/lib/types.ts delete mode 100644 packages/interactive-code-block/src/lib/utils.ts delete mode 100644 packages/interactive-code-block/tsconfig.json delete mode 100644 packages/interactive-code-block/tsconfig.lib.json delete mode 100644 packages/interactive-code-block/tsconfig.spec.json delete mode 100644 packages/interactive-code-block/vite.config.ts diff --git a/packages/interactive-code-block/.eslintrc.json b/packages/interactive-code-block/.eslintrc.json deleted file mode 100644 index dd6da33e2d..0000000000 --- a/packages/interactive-code-block/.eslintrc.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.ts", "*.tsx"], - "rules": {} - }, - { - "files": ["*.js", "*.jsx"], - "rules": {} - } - ] -} diff --git a/packages/interactive-code-block/README.md b/packages/interactive-code-block/README.md deleted file mode 100644 index eb511df802..0000000000 --- a/packages/interactive-code-block/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# interactive-code-block - -This library was generated with [Nx](https://nx.dev). - -## Building - -Run `nx build interactive-code-block` to build the library. - -## Running unit tests - -Run `nx test interactive-code-block` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/packages/interactive-code-block/package.json b/packages/interactive-code-block/package.json deleted file mode 100644 index 3d9cde34e5..0000000000 --- a/packages/interactive-code-block/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "@wp-playground/interactive-code-block", - "version": "0.0.1", - "type": "module", - "private": true -} diff --git a/packages/interactive-code-block/project.json b/packages/interactive-code-block/project.json deleted file mode 100644 index 5e70937a42..0000000000 --- a/packages/interactive-code-block/project.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "interactive-code-block", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "packages/interactive-code-block/src", - "projectType": "library", - "targets": { - "build": { - "executor": "nx:noop", - "dependsOn": ["build:zip"] - }, - "build:zip": { - "executor": "nx:run-commands", - "options": { - "cwd": "dist/packages", - "commands": [ - "rm interactive-code-block.zip || true", - "zip interactive-code-block.zip -r interactive-code-block" - ] - }, - "dependsOn": ["build:bundle"] - }, - "build:bundle": { - "executor": "@nx/vite:build", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/packages/interactive-code-block" - } - }, - "dev": { - "executor": "@nx/vite:build", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/packages/interactive-code-block", - "watch": true, - "minify": false - } - }, - "test": { - "executor": "@nx/vite:test", - "outputs": ["coverage/packages/interactive-code-block"], - "options": { - "passWithNoTests": true, - "reportsDirectory": "../../coverage/packages/interactive-code-block" - } - }, - "lint": { - "executor": "@nx/linter:eslint", - "outputs": ["{options.outputFile}"], - "options": { - "lintFilePatterns": ["packages/interactive-code-block/**/*.ts"] - } - } - }, - "tags": [] -} diff --git a/packages/interactive-code-block/public/block.php b/packages/interactive-code-block/public/block.php deleted file mode 100644 index ad1b66faeb..0000000000 --- a/packages/interactive-code-block/public/block.php +++ /dev/null @@ -1,115 +0,0 @@ - $asset_file['files']['view'], - 'interactive-code-block-editor' => $asset_file['files']['editor'], - ]; - foreach ($scripts as $script => $file) { - wp_register_script( - $script, - plugins_url($file, __FILE__), - $deps, - '' // Version is included in the file name - ); - } - - // Load block scripts as ES modules - add_filter('script_loader_tag', function ($tag, $handle, $src) use ($scripts) { - if (!array_key_exists($handle, $scripts)) { - return $tag; - } - - // Remove any query string from the URL - $new_url = current(explode('?', $src)); - $tag = ''; - return $tag; - }, 10, 3); - - $asset_file = require plugin_dir_path(__FILE__) . 'index.asset.php'; - $deps = []; - foreach ($asset_file['dependencies'] as $dep) { - if (wp_style_is($dep, 'registered')) { - $deps[] = $dep; - } - } - - // Register block styles - $styles = [ - 'interactive-code-block' => 'view', - 'interactive-code-block-editor' => 'editor', - ]; - foreach ($styles as $style => $file) { - wp_register_style( - $style, - plugins_url('assets/' . $file . '.css', __FILE__), - $deps, - $asset_file['version'] - ); - } - - // Register the block itself - register_block_type_from_metadata(__DIR__ . '/assets/block.json', [ - 'render_callback' => function ($attributes) { - if (!isset($attributes['code'])) { - return ''; - } - - $execution_scripts = get_option(LCS_Execution_Scripts_Endpoint::OPTION_KEY, []); - $script_id = $attributes['executionScript']; - $script = $execution_scripts[$script_id] ?? ''; - - $all_libraries = lcs_get_libraries_list(); - $library_ids = array_unique( - array_merge( - $attributes['libraries'], - $script['libraries'] ?? [] - ) - ); - $libraries = array_values(array_filter($all_libraries, function ($library) use ($library_ids) { - return in_array($library['id'], $library_ids); - })); - - // The base64_decode is to prevent incorrect escaping by Gutenberg - // E.g. representing

seems - // to be a problem for Gutenberg – it will store it as - // <h1 title="&lt;div&gt; &quot;html&quot;"$gt; - return ' -
- Loading interactive code snippet... -
- '; - } - // Render a static code snippet while the interactive one is loading: - // Useful only for PHP and only if the PHP version >= webhost version. - // ' . highlight_string(base64_decode($attributes['code']), true) . ' - ]); -}); - - -function lce_editor_init($hook) -{ - global $current_screen; - if (!$current_screen->is_block_editor()) { - return; - } - - lce_preload_endpoints_data(); -} -add_action('admin_enqueue_scripts', 'lce_editor_init'); \ No newline at end of file diff --git a/packages/interactive-code-block/public/interactive-code-block.php b/packages/interactive-code-block/public/interactive-code-block.php deleted file mode 100644 index 5b2b75e56e..0000000000 --- a/packages/interactive-code-block/public/interactive-code-block.php +++ /dev/null @@ -1,115 +0,0 @@ - $asset_file['files']['execution-scripts'], - 'interactive-code-block-libraries-page' => $asset_file['files']['libraries'], - ]; - foreach ($asset_file['dependencies'] as $dep) { - if (wp_script_is($dep, 'registered')) { - $deps[] = $dep; - } - } - foreach ($scripts as $script => $file) { - wp_register_script( - $script, - plugins_url($file, __FILE__), - $deps, - $asset_file['version'] - ); - } - - // Load scripts as ES modules - add_filter('script_loader_tag', function($tag, $handle, $src) use ($scripts) { - if(!array_key_exists($handle, $scripts)) { - return $tag; - } - $tag = ''; - return $tag; - }, 10, 3); - - require_once __DIR__ . '/rest-api-lcs-libraries-endpoint.php'; - $lcs_libraries_endpoint = new LCS_Libraries_Endpoint(); - add_action('rest_api_init', array($lcs_libraries_endpoint, 'register_routes')); - - require_once __DIR__ . '/rest-api-lcs-execution-scripts-endpoint.php'; - $lcs_execution_scripts = new LCS_Execution_Scripts_Endpoint(); - add_action('rest_api_init', array($lcs_execution_scripts, 'register_routes')); -}); - -function lce_setup_admin_menu() -{ - // Add a top-level menu item that has two nested items named differently than the top-level menu - add_menu_page('Interactive Code Block', 'Interactive Code Block', 'manage_options', 'interactive-code-block--execution-scripts', 'lce_execution_scripts_page', 'dashicons-editor-code', 6); - add_submenu_page('interactive-code-block--execution-scripts', 'Execution Scripts', 'Execution Scripts', 'manage_options', 'interactive-code-block--execution-scripts', 'lce_execution_scripts_page'); - add_submenu_page('interactive-code-block--execution-scripts', 'Libraries', 'Libraries', 'manage_options', 'interactive-code-block--libraries', 'lce_libraries_page'); -} - -function lce_execution_scripts_page() -{ - echo ' -

Execution Scripts

-
- '; -} - -function lce_libraries_page() -{ - echo ' -

Phar libraries

-
- '; -} - -add_action('admin_menu', 'lce_setup_admin_menu'); - -function load_custom_wp_admin_scripts($hook) -{ - if ('interactive-code-block_page_interactive-code-block--libraries' === $hook) { - wp_enqueue_script('interactive-code-block-libraries-page'); - wp_enqueue_style('interactive-code-block-editor'); - lce_preload_endpoints_data(); - } else if ('toplevel_page_interactive-code-block--execution-scripts' === $hook) { - wp_enqueue_script('interactive-code-block-execution-scripts-page'); - wp_enqueue_style('interactive-code-block-editor'); - lce_preload_endpoints_data(); - } -} - -add_action('admin_enqueue_scripts', 'load_custom_wp_admin_scripts'); - -function lce_preload_endpoints_data() -{ - // Preload block editor paths. - // most of these are copied from edit-forms-blocks.php. - $preload_paths = array( - '/interactive-code-block/v1/libraries?context=edit', - '/interactive-code-block/v1/execution-scripts?context=edit', - ); - $preload_data = array_reduce( - $preload_paths, - 'rest_preload_api_request', - array() - ); - wp_add_inline_script( - 'wp-api-fetch', - sprintf('wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', wp_json_encode($preload_data)), - 'after' - ); -} - - -require_once __DIR__ . '/block.php'; diff --git a/packages/interactive-code-block/public/playground-sandbox.html b/packages/interactive-code-block/public/playground-sandbox.html deleted file mode 100644 index ac6bc252f3..0000000000 --- a/packages/interactive-code-block/public/playground-sandbox.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/interactive-code-block/public/rest-api-lcs-execution-scripts-endpoint.php b/packages/interactive-code-block/public/rest-api-lcs-execution-scripts-endpoint.php deleted file mode 100644 index 6913f1cafc..0000000000 --- a/packages/interactive-code-block/public/rest-api-lcs-execution-scripts-endpoint.php +++ /dev/null @@ -1,218 +0,0 @@ - 'GET', - 'callback' => [$this, 'get_execution_scripts'], - 'permission_callback' => [$this, 'has_permission'], - ]); - - register_rest_route('interactive-code-block/v1', '/execution-scripts', [ - 'methods' => 'POST', - 'callback' => [$this, 'create_execution_script'], - 'permission_callback' => [$this, 'has_permission'], - 'args' => [ - 'name' => [ - 'required' => true, - 'validate_callback' => function ($param, $request, $key) { - return is_string($param); - }, - ], - 'content' => [ - 'required' => true, - 'validate_callback' => function ($param, $request, $key) { - return is_string($param); - }, - ], - 'runner' => [ - 'required' => true, - 'validate_callback' => function ($param, $request, $key) { - return in_array($param, VALID_CODE_RUNNERS); - }, - ], - 'outputFormat' => [ - 'validate_callback' => function ($param, $request, $key) { - return in_array($param, VALID_OUTPUT_FORMATS); - }, - ], - 'libraries' => [ - 'required' => false, - 'validate_callback' => function ($param, $request, $key) { - if (!is_array($param)) { - return false; - } - foreach ($param as $library) { - if (!is_string($library)) { - return false; - } - } - return true; - }, - ], - ], - ]); - - register_rest_route('interactive-code-block/v1', '/execution-scripts/(?P[a-z0-9]+)', [ - 'methods' => 'GET', - 'callback' => [$this, 'get_execution_script'], - 'permission_callback' => [$this, 'has_permission'], - ]); - - register_rest_route('interactive-code-block/v1', '/execution-scripts/(?P[a-z0-9]+)', [ - 'methods' => 'PUT', - 'callback' => [$this, 'update_execution_script'], - 'permission_callback' => [$this, 'has_permission'], - 'args' => [ - 'name' => [ - 'validate_callback' => function ($param, $request, $key) { - return is_string($param); - }, - ], - 'runner' => [ - 'validate_callback' => function ($param, $request, $key) { - return in_array($param, VALID_CODE_RUNNERS); - }, - ], - 'outputFormat' => [ - 'validate_callback' => function ($param, $request, $key) { - return in_array($param, VALID_OUTPUT_FORMATS); - }, - ], - 'content' => [ - 'validate_callback' => function ($param, $request, $key) { - return is_string($param); - }, - ], - ], - ]); - - register_rest_route('interactive-code-block/v1', '/execution-scripts/(?P[a-z0-9]+)', [ - 'methods' => 'DELETE', - 'callback' => [$this, 'delete_execution_script'], - 'permission_callback' => [$this, 'has_permission'], - ]); - } - - public function has_permission() - { - return current_user_can('upload_files') && current_user_can('edit_posts'); - } - - public function get_execution_scripts() - { - return array_values(get_option(self::OPTION_KEY, [])); - } - - public function create_execution_script($request) - { - $scripts = get_option(self::OPTION_KEY, []); - - do { - $id = uniqid(); - } while (array_key_exists($id, $scripts)); - - $scripts[$id] = [ - 'id' => $id, - 'name' => $request->get_param('name'), - 'runner' => $request->get_param('runner'), - 'outputFormat' => $request->get_param('outputFormat'), - 'content' => $request->get_param('content'), - 'libraries' => $request->get_param('libraries') ?: [], - ]; - - update_option(self::OPTION_KEY, $scripts); - - return $scripts[$id]; - } - - public function get_execution_script($request) - { - $id = $request['id']; - $scripts = get_option(self::OPTION_KEY, []); - - if (!array_key_exists($id, $scripts)) { - return new WP_Error('rest_invalid_param', 'Execution script not found.', ['status' => 404]); - } - - return $scripts[$id]; - } - - public function update_execution_script($request) - { - $id = $request['id']; - $new_name = $request->get_param('name'); - $new_runner = $request->get_param('runner'); - $new_content = $request->get_param('content'); - $new_libraries = $request->get_param('libraries'); - - $scripts = get_option(self::OPTION_KEY, []); - - if (!array_key_exists($id, $scripts)) { - return new WP_Error('rest_invalid_param', 'Execution script not found.', ['status' => 404]); - } - - if ($new_name) { - $scripts[$id]['name'] = $new_name; - } - if ($new_runner) { - $scripts[$id]['runner'] = $new_runner; - } - if ($new_content) { - $scripts[$id]['content'] = $new_content; - } - if ($new_libraries) { - $scripts[$id]['libraries'] = $new_libraries; - } - if ($request->has_param('outputFormat')) { - $scripts[$id]['outputFormat'] = $request->get_param('outputFormat'); - } - update_option(self::OPTION_KEY, $scripts); - - return $scripts[$id]; - } - - public function delete_execution_script($request) - { - $id = $request['id']; - - $scripts = get_option(self::OPTION_KEY, []); - - if (!array_key_exists($id, $scripts)) { - return new WP_Error('rest_invalid_param', 'Execution script not found.', ['status' => 404]); - } - - unset($scripts[$id]); - update_option(self::OPTION_KEY, $scripts); - - return true; - } -} \ No newline at end of file diff --git a/packages/interactive-code-block/public/rest-api-lcs-libraries-endpoint.php b/packages/interactive-code-block/public/rest-api-lcs-libraries-endpoint.php deleted file mode 100644 index 93f27c2465..0000000000 --- a/packages/interactive-code-block/public/rest-api-lcs-libraries-endpoint.php +++ /dev/null @@ -1,233 +0,0 @@ -uploads_dir = wp_upload_dir()['basedir'] . static::UPLOAD_DIR_SUFFIX; - $this->uploads_url = wp_upload_dir()['baseurl'] . static::UPLOAD_DIR_SUFFIX; - - if (!file_exists($this->uploads_dir)) { - mkdir($this->uploads_dir); - } - } - - public function register_routes() - { - register_rest_route('interactive-code-block/v1', '/libraries', [ - 'methods' => 'GET', - 'callback' => [$this, 'get_libraries'], - 'permission_callback' => [$this, 'has_permission'], - ]); - - register_rest_route('interactive-code-block/v1', '/libraries', [ - 'methods' => 'POST', - 'callback' => [$this, 'upload_library'], - 'permission_callback' => [$this, 'has_permission'], - ]); - - register_rest_route('interactive-code-block/v1', '/libraries/(?P[a-z0-9]+)', [ - 'methods' => 'GET', - 'callback' => [$this, 'get_library'], - 'permission_callback' => [$this, 'has_permission'], - 'args' => [ - 'id' => [ - 'required' => true, - 'validate_callback' => [$this, 'validate_id'], - ], - ], - ]); - register_rest_route('interactive-code-block/v1', '/libraries/(?P[a-z0-9]+)', [ - 'methods' => 'DELETE', - 'callback' => [$this, 'delete_library'], - 'permission_callback' => [$this, 'has_permission'], - 'args' => [ - 'id' => [ - 'required' => true, - 'validate_callback' => [$this, 'validate_id'], - ], - ], - ]); - register_rest_route('interactive-code-block/v1', '/libraries/(?P[a-z0-9]+)', [ - 'methods' => ['PUT', 'POST'], - 'callback' => [$this, 'update_library'], - 'permission_callback' => [$this, 'has_permission'], - 'args' => [ - 'id' => [ - 'required' => true, - 'validate_callback' => [$this, 'validate_id'], - ], - 'name' => [], - ], - ]); - - } - - public function has_permission() - { - return current_user_can('upload_files') && current_user_can('edit_posts'); - } - - public function get_libraries($request) - { - return lcs_get_libraries_list(); - } - - public function upload_library($request) - { - $file = $this->sanitize_file($request); - if (is_wp_error($file)) { - return $file; - } - - // If name already stored, replace the file - $libraries = get_option(self::OPTION_KEY, []); - foreach ($libraries as $library) { - if ($library['name'] === $file['name']) { - $file_path = $this->uploads_dir . "/" . $library['id']; - if (!move_uploaded_file($file['tmp_name'], $file_path)) { - return new WP_Error('rest_server_error', 'Failed to move uploaded file.', ['status' => 500]); - } - return $library; - } - } - - do { - $file_name = uniqid(); - } while (file_exists($this->uploads_dir . $file_name)); - $file_path = $this->uploads_dir . "/$file_name"; - - // Move the uploaded file to the uploads directory - if (!move_uploaded_file($file['tmp_name'], $file_path)) { - return new WP_Error('rest_server_error', 'Failed to move uploaded file.', ['status' => 500]); - } - - // Add the file to the list of uploaded libraries - $libraries = get_option(self::OPTION_KEY, []); - $libraries[$file_name] = [ - 'id' => $file_name, - 'name' => $file['name'], - 'type' => $file['type'], - 'size' => $file['size'], - ]; - update_option(self::OPTION_KEY, $libraries); - - return $libraries[$file_name]; - } - - public function update_library($request) - { - $id = $request['id']; - - $libraries = get_option(self::OPTION_KEY, []); - if (isset($request['name'])) { - $libraries[$id]['name'] = $request['name']; - } - - if (isset($request['file'])) { - $file = $this->sanitize_file($request); - if (is_wp_error($file)) { - return $file; - } - - // Replace the previous library file - $file_path = $this->uploads_dir . "/$id"; - if (!move_uploaded_file($file['tmp_name'], $file_path)) { - return new WP_Error('rest_server_error', 'Failed to move uploaded file.', ['status' => 500]); - } - - // Update stored file details - $libraries[$id]['type'] = $file['type']; - $libraries[$id]['size'] = $file['size']; - } - update_option(self::OPTION_KEY, $libraries); - - return $libraries[$id]; - } - - public function get_library($request) - { - $id = $request['id']; - $libraries = lcs_get_libraries_list(); - return $libraries[$id]; - } - - public function delete_library($request) - { - $id = $request['id']; - - // Remove the file from the list of uploaded files - $libraries = get_option(self::OPTION_KEY, []); - unset($libraries[$id]); - update_option(self::OPTION_KEY, $libraries); - - // Delete the file from the uploads directory - unlink($this->uploads_dir . "/$id"); - - return true; - } - - public function sanitize_file($request) - { - $files = $request->get_file_params(); - - if (empty($files['file'])) { - return new WP_Error('rest_server_error', 'No file was uploaded.', ['status' => 400]); - } - - $file = $files['file']; - - if (is_wp_error($file)) { - return new WP_Error('rest_server_error', 'There was an error uploading a file.', ['status' => 400]); - } - - // Check if the uploaded file is smaller than the max size - $max_size = wp_max_upload_size(); - if ($file['size'] > $max_size) { - return new WP_Error('rest_invalid_param', 'File is too large.', ['status' => 400]); - } - - return $file; - } - - public function validate_id($id, $request, $key) - { - $files = get_option(self::OPTION_KEY, []); - - // Check if the file with the specified ID exists - if (!array_key_exists($id, $files)) { - return new WP_Error('rest_invalid_param', 'File not found.', ['status' => 404]); - } - - return true; - } - -} - -function lcs_get_libraries_list() -{ - $libraries = array_values(get_option(LCS_Libraries_Endpoint::OPTION_KEY, [])); - usort($libraries, function ($a, $b) { - return strcmp(strtolower($a['name']), strtolower($b['name'])); - }); - $uploads_url = wp_upload_dir()['baseurl'] . LCS_Libraries_Endpoint::UPLOAD_DIR_SUFFIX; - foreach ($libraries as $k => $library) { - $libraries[$k]['url'] = $uploads_url . "/{$library['id']}"; - } - return $libraries; -} \ No newline at end of file diff --git a/packages/interactive-code-block/src/index.ts b/packages/interactive-code-block/src/index.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/interactive-code-block/src/lib/block/base64-utils.ts b/packages/interactive-code-block/src/lib/block/base64-utils.ts deleted file mode 100644 index d8f4985b8f..0000000000 --- a/packages/interactive-code-block/src/lib/block/base64-utils.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function utf8ToBase64(str: string) { - return window.btoa(unescape(encodeURIComponent(str))); -} - -export function base64ToUtf8(str: string) { - return decodeURIComponent(escape(window.atob(str))); -} diff --git a/packages/interactive-code-block/src/lib/block/block.json b/packages/interactive-code-block/src/lib/block/block.json deleted file mode 100644 index bbb3a0d102..0000000000 --- a/packages/interactive-code-block/src/lib/block/block.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "$schema": "https://schemas.wp.org/trunk/block.json", - "name": "wordpress-playground/interactive-code", - "title": "Interactive Code", - "icon": "shield", - "category": "common", - "attributes": { - "code": { - "type": "string" - }, - "fileType": { - "type": "string", - "enum": ["php", "sql", "js"], - "default": "php" - }, - "executionScript": { - "type": "string", - "default": "PHP" - }, - "libraries": { - "type": "array", - "default": [] - }, - "cachedOutput": { - "type": "string" - }, - "showCachedOutput": { - "type": "boolean", - "default": true - } - }, - "viewScript": "interactive-code-block", - "editorScript": "interactive-code-block-editor", - "style": "interactive-code-block", - "editorStyle": "interactive-code-block-editor" -} diff --git a/packages/interactive-code-block/src/lib/block/edit.tsx b/packages/interactive-code-block/src/lib/block/edit.tsx deleted file mode 100644 index 5b18b53285..0000000000 --- a/packages/interactive-code-block/src/lib/block/edit.tsx +++ /dev/null @@ -1,229 +0,0 @@ -import * as React from 'react'; -import { useCallback, useMemo } from '@wordpress/element'; -import { useEntityRecords } from '@wordpress/core-data'; -import type { BlockEditProps } from '@wordpress/blocks'; -import { BlockControls, InspectorControls } from '@wordpress/block-editor'; - -import { - ToolbarGroup, - ToggleControl, - SelectControl, - Spinner, - PanelBody, - PanelRow, -} from '@wordpress/components'; -import { settings } from '@wordpress/icons'; - -import LibrariesControl from '../components/libraries-control'; -import { InteractiveCodeSnippet } from '../components/interactive-code-snippet'; -import { base64ToUtf8, utf8ToBase64 } from './base64-utils'; -import ToolbarDropdown from '../components/toolbar-dropdown'; -import { - ExecutionScript, - Library, - InteractiveCodeSnippetBlockAttributes, -} from '../types'; -import { - getDefaultExecutionScript, - SUPPORTED_RUNNERS, -} from '../components/code-runner'; -import { WithoutGutenbergKeyboardShortcuts } from './without-gutenberg-keyboard-shortcuts'; - -// CSS isn't injected into the block editor without -// putting this asynchronous import here (this is -// probably a bug in Vite): -document.addEventListener('DOMContentLoaded', () => { - import('../components/interactive-code-snippet'); -}); - -export const SUPPORTED_HIGHLIGHTERS = [ - { label: 'PHP', value: 'php' }, - { label: 'SQL', value: 'sql' }, - { label: 'JavaScript', value: 'js' }, -]; - -export default function EditInteractiveCodeSnippet({ - attributes, - isSelected, - setAttributes, -}: BlockEditProps) { - const executionScripts = useEntityRecords( - 'interactive-code-block', - 'script' - ); - function getExecutionScript(scriptId) { - return ( - getDefaultExecutionScript(scriptId) || - executionScripts.records?.find((script) => script.id === scriptId) - ); - } - const usedExecutionScript = - getExecutionScript(attributes.executionScript) || executionScripts?.[0]; - // Reset libraries if the runner changes - const setExecutionScript = useCallback( - (newScriptName: string) => { - const newScript = getExecutionScript(newScriptName); - if ( - newScript && - usedExecutionScript?.runner !== newScript?.runner - ) { - setAttributes({ libraries: [] }); - } - setAttributes({ executionScript: newScriptName }); - }, - [usedExecutionScript] - ); - - const libraries = useEntityRecords( - 'interactive-code-block', - 'library' - ); - const usedLibraries = useMemo( - () => - libraries.records?.filter( - (library) => - attributes.libraries.includes(library.id) || - usedExecutionScript?.libraries?.includes(library.id) - ), - [attributes.libraries, libraries.hasResolved] - ); - - const handleCodeChange = useCallback((newCode: string) => { - setAttributes({ code: utf8ToBase64(newCode) }); - }, []); - - const handleCacheOutput = useCallback((output: string) => { - setAttributes({ cachedOutput: utf8ToBase64(output) }); - }, []); - - return ( - <> - - - - setAttributes({ fileType }) - } - options={SUPPORTED_HIGHLIGHTERS.map((highlighter) => ({ - label: `Highlighting: ${highlighter.label}`, - value: highlighter.value, - }))} - /> - - - - - - - setAttributes({ - showCachedOutput: - !attributes.showCachedOutput, - }) - } - checked={attributes.showCachedOutput} - /> - - - setAttributes({ fileType })} - /> - - - - - - - setAttributes({ libraries }) - } - /> - - - - {!usedLibraries || !usedExecutionScript ? ( - - ) : ( - - - - )} - - ); -} - -function decodeAttr(value: string) { - if (!value) { - return ''; - } - - try { - return base64ToUtf8(value); - } catch (e) { - return value; - } -} - -interface ExecutionScriptControlProps { - selected: string; - onChange: (value: string) => void; -} - -function ExecutionScriptControl({ - selected, - onChange, -}: ExecutionScriptControlProps) { - const executionScripts = useEntityRecords( - 'interactive-code-block', - 'script' - ); - - if (executionScripts.records === null) { - return ; - } - - const defaultExecutionScripts = SUPPORTED_RUNNERS.map((runner) => ({ - label: runner.defaultExecutionScript.name!, - value: runner.defaultExecutionScript.id, - })); - const userCreatedScripts = executionScripts.records.map( - (script: ExecutionScript) => ({ - label: script.name || '', - value: script.id, - }) - ); - - const options = [...defaultExecutionScripts, ...userCreatedScripts]; - - return ( - - ); -} diff --git a/packages/interactive-code-block/src/lib/block/editor.css b/packages/interactive-code-block/src/lib/block/editor.css deleted file mode 100644 index 09a343a211..0000000000 --- a/packages/interactive-code-block/src/lib/block/editor.css +++ /dev/null @@ -1,27 +0,0 @@ -.interactive-code-snippet-items-list { - margin: 0; - padding: 0; - list-style: none; - - display: flex; - flex-direction: column; - gap: 5px; -} - -.interactive-code-snippet-items-list-wrapper { - max-width: 400px !important; -} - -.interactive-code-block-form__buttons { - margin-top: 10px; -} - -.interactive-code-block__snackbar-list { - position: fixed; - bottom: 20px; -} - -.interactive-code-block__panel-row .components-input-control__label { - padding-right: 10px; - text-overflow: unset !important; -} diff --git a/packages/interactive-code-block/src/lib/block/editor.tsx b/packages/interactive-code-block/src/lib/block/editor.tsx deleted file mode 100644 index 104b2ec99b..0000000000 --- a/packages/interactive-code-block/src/lib/block/editor.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import * as React from '@wordpress/element'; -import { useBlockProps } from '@wordpress/block-editor'; -import { registerBlockType } from '@wordpress/blocks'; -import { installEntities } from '../pages/entities'; - -import EditInteractiveCodeSnippet from './edit'; - -import * as metadata from './block.json'; -import { InteractiveCodeSnippetBlockAttributes } from '../types'; - -// Rollup crashes if this import, that is only needed -// in hooks/use-php.ts, isn't repeated here: -// @ts-ignore -import '../php-worker?url&worker'; - -// Rollup crashes if this import, that is only needed -// in hooks/use-php.ts, isn't repeated here: -// @ts-ignore -import '../block/view?url&worker'; - -// Make vite copy the block.json file to the build folder: -// @ts-ignore -export * as blockJsonUrl from './block.json?url'; - -const { name } = metadata; - -export { metadata, name }; - -installEntities(); -registerBlockType(metadata as any, { - icon: 'shield', - edit: EditInteractiveCodeSnippet, - save: ({ attributes }) => { - return
{attributes.code}
; - }, -}); diff --git a/packages/interactive-code-block/src/lib/block/php-wasm.php b/packages/interactive-code-block/src/lib/block/php-wasm.php deleted file mode 100644 index 8b23c6c837..0000000000 --- a/packages/interactive-code-block/src/lib/block/php-wasm.php +++ /dev/null @@ -1,25 +0,0 @@ -, - elem - ); -} diff --git a/packages/interactive-code-block/src/lib/block/view.css b/packages/interactive-code-block/src/lib/block/view.css deleted file mode 100644 index e0b82707ef..0000000000 --- a/packages/interactive-code-block/src/lib/block/view.css +++ /dev/null @@ -1,51 +0,0 @@ -.has-interactive-code-spinner { - position: relative; -} - -.has-interactive-code-spinner:before { - content: ''; - display: block; - position: absolute; - z-index: 1; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(255, 255, 255, 0.7); -} - -.has-interactive-code-spinner:after { - content: ''; - display: block; - height: 60px; - width: 60px; - background-image: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjQwIiB2aWV3Ym94PSIwIDAgNDAgNDAiIHdpZHRoPSI0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8c3R5bGU+CiAgICBwYXRoIHsKICAgICAgLXdlYmtpdC1hbmltYXRpb246IHJvdGF0ZSAxcyBsaW5lYXIgaW5maW5pdGU7CiAgICAgIC1tb3otYW5pbWF0aW9uOiByb3RhdGUgMXMgbGluZWFyIGluZmluaXRlOwogICAgICBhbmltYXRpb246IHJvdGF0ZSAxcyBsaW5lYXIgaW5maW5pdGU7CiAgICAgIHRyYW5zZm9ybS1vcmlnaW46IDIwcHggMjBweDsKICAgIH0KICAgIEAtd2Via2l0LWtleWZyYW1lcyByb3RhdGUgewogICAgICAxMDAlIHsKICAgICAgICB0cmFuc2Zvcm06IHJvdGF0ZSgzNjBkZWcpCiAgICAgIH0KICAgIH0KICAgIEBrZXlmcmFtZXMgcm90YXRlIHsKICAgICAgMTAwJSB7CiAgICAgICAgdHJhbnNmb3JtOiByb3RhdGUoMzYwZGVnKQogICAgICB9CiAgICB9CiAgPC9zdHlsZT4KICA8ZyBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iNCI+CiAgICA8Y2lyY2xlIGN4PSI1MCUiIGN5PSI1MCUiIHI9IjE4IiBzdHJva2Utb3BhY2l0eT0iMC4zIj48L2NpcmNsZT4KICAgIDxwYXRoIGQ9Ik0yMCwyIEExOCwxOCAwIDAsMSAzOCwyMCI+PC9wYXRoPgogIDwvZz4KPC9zdmc+Cg==); - background-size: contain; - position: absolute; - z-index: 2; - left: calc(50% - 30px); - top: calc(50% - 30px); - animation: spinner-rotate 2s ease-in infinite; - animation-timing-function: linear; -} - -.has-interactive-code-spinner:before, -.has-interactive-code-spinner:after { - opacity: 0; - pointer-events: none; - transition: opacity ease-in 0.2s; -} - -.has-interactive-code-spinner.is-spinner-active:before, -.has-interactive-code-spinner.is-spinner-active:after { - opacity: 1; -} - -@keyframes spinner-rotate { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/packages/interactive-code-block/src/lib/block/view.ts b/packages/interactive-code-block/src/lib/block/view.ts deleted file mode 100644 index 409276783d..0000000000 --- a/packages/interactive-code-block/src/lib/block/view.ts +++ /dev/null @@ -1,3 +0,0 @@ -document.addEventListener('DOMContentLoaded', () => { - import('./view-async'); -}); diff --git a/packages/interactive-code-block/src/lib/block/without-gutenberg-keyboard-shortcuts.tsx b/packages/interactive-code-block/src/lib/block/without-gutenberg-keyboard-shortcuts.tsx deleted file mode 100644 index cf7be53cf7..0000000000 --- a/packages/interactive-code-block/src/lib/block/without-gutenberg-keyboard-shortcuts.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import * as React from 'react'; -import { useEffect } from '@wordpress/element'; -import { KeyboardShortcuts } from '@wordpress/components'; -import { rawShortcut } from '@wordpress/keycodes'; - -const stop = (e) => e.stopImmediatePropagation(); - -const characters = [ - 'a', - 'b', - 'c', - 'd', - 'e', - 'f', - 'g', - 'h', - 'i', - 'j', - 'k', - 'l', - 'm', - 'n', - 'o', - 'p', - 'q', - 'r', - 's', - 't', - 'u', - 'v', - 'w', - 'x', - 'y', - 'z', - 'tab', - '[', - ']', - '{', - '}', -]; - -const shortcutOverrides = Object.fromEntries( - characters.flatMap((character) => [ - [character, stop], - [rawShortcut.access(character), stop], - [rawShortcut.ctrl(character), stop], - [rawShortcut.ctrlShift(character), stop], - [rawShortcut.primary(character), stop], - [rawShortcut.primaryShift(character), stop], - [rawShortcut.primaryAlt(character), stop], - ]) -); - -interface WithoutGutenbergKeyboardShortcutsProps { - isSelected: boolean; - children?: any; -} - -export function WithoutGutenbergKeyboardShortcuts({ - children, - isSelected, -}: WithoutGutenbergKeyboardShortcutsProps) { - useEffect(() => { - // Disable the global copy handlers when the block is selected. - if (isSelected) { - // Capture: true ensures this gets triggered before Gutenberg's - // copy handler. - document.addEventListener('copy', stop, { capture: true }); - document.addEventListener('cut', stop, { capture: true }); - document.addEventListener('paste', stop, { capture: true }); - return () => { - document.removeEventListener('copy', stop, { capture: true }); - document.removeEventListener('cut', stop, { capture: true }); - document.removeEventListener('paste', stop, { capture: true }); - }; - } - }, [isSelected]); - // Prevent regular keyboard shortcuts from bubbling to the editor: - return ( - - {children} - - ); -} diff --git a/packages/interactive-code-block/src/lib/components/code-mirror/index.tsx b/packages/interactive-code-block/src/lib/components/code-mirror/index.tsx deleted file mode 100644 index 8141ac127c..0000000000 --- a/packages/interactive-code-block/src/lib/components/code-mirror/index.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import type { ViewUpdate } from '@codemirror/view'; -import { - EditorView, - keymap, - highlightSpecialChars, - drawSelection, - highlightActiveLine, - dropCursor, - rectangularSelection, - crosshairCursor, - lineNumbers, - highlightActiveLineGutter, -} from '@codemirror/view'; -import { - defaultHighlightStyle, - syntaxHighlighting, - indentOnInput, - bracketMatching, - foldGutter, - foldKeymap, -} from '@codemirror/language'; -import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'; -import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete'; -import { oneDark } from '@codemirror/theme-one-dark'; - -import { - useState, - useMemo, - useEffect, - useRef, - useImperativeHandle, - forwardRef, - memo, -} from '@wordpress/element'; - -import type { - ForwardRefExoticComponent, - MemoExoticComponent, - RefAttributes, -} from 'react'; - -export interface CodeMirrorProps { - onChange?: (updatedContents: string) => void; - onSave?: (updatedContents: string) => void; - initialContents: string; - fileType: 'php' | 'sql' | 'js'; - className?: string; -} - -export type CodeMirrorRef = { - getContents: () => string; -}; - -const CodeMirror: MemoExoticComponent< - ForwardRefExoticComponent> -> = memo( - forwardRef(function CodeMirror( - { onChange, onSave, initialContents, fileType, className = '' }, - ref - ) { - const codeMirrorRef = useRef(null); - const contentsRef = useRef(initialContents); - const onChangeRef = useRef(onChange); - const languagePlugin = useLanguagePlugin(fileType); - // For rerendering: - const [dep, setDep] = useState({}); - - useImperativeHandle(ref, () => ({ - getContents: () => contentsRef.current, - })); - useEffect(() => { - onChangeRef.current = onChange; - }, [onChange]); - - const view = useMemo(() => { - if (!codeMirrorRef.current) { - // Rerender - setTimeout(() => setDep({})); - return null; - } - - const themeOptions = EditorView.theme({ - '&': { - height: 'auto', - width: '100%', - }, - }); - - const ourKeymap = (keymap as any).of([ - { - key: 'Mod-s', - run() { - if (typeof onSave === 'function') { - onSave(contentsRef.current); - } - return true; - }, - }, - ...closeBracketsKeymap, - ...defaultKeymap, - ...historyKeymap, - ...foldKeymap, - ]); - - const updateListener = EditorView.updateListener.of( - (vu: ViewUpdate) => { - if (vu.docChanged) { - contentsRef.current = vu.state.doc.toString(); - if (typeof onChangeRef.current === 'function') { - onChangeRef.current(contentsRef.current); - } - } - } - ); - - const extensions = [ - lineNumbers(), - highlightActiveLineGutter(), - highlightSpecialChars(), - history(), - foldGutter(), - drawSelection(), - dropCursor(), - indentOnInput(), - syntaxHighlighting(defaultHighlightStyle, { - fallback: true, - }), - bracketMatching(), - closeBrackets(), - rectangularSelection(), - crosshairCursor(), - highlightActiveLine(), - oneDark, - themeOptions, - ourKeymap, - ]; - - if (languagePlugin) { - extensions.push(languagePlugin()); - } - extensions.push(updateListener); - - const _view = new EditorView({ - doc: initialContents, - extensions, - parent: codeMirrorRef.current, - }); - - return _view; - }, [languagePlugin, dep, codeMirrorRef.current]); - - useEffect(() => { - // return a function to be executed at component unmount - return () => { - view && view.destroy(); - }; - }, [view]); - - return
; - }) -); - -export default CodeMirror; - -function useLanguagePlugin(language: string): any { - const lastLanguageRef = useRef(language); - const [plugin, setPlugin] = useState(null); - useEffect(() => { - lastLanguageRef.current = language; - importLanguagePlugin(language).then((pluginFactory) => { - if (lastLanguageRef.current === language) { - setPlugin(() => pluginFactory); - } - }); - }, [language]); - return plugin; -} - -async function importLanguagePlugin(fileType: string): Promise { - switch (fileType) { - case 'js': - return (await import('@codemirror/lang-javascript')).javascript; - case 'php': - return (await import('@codemirror/lang-php')).php; - case 'sql': - return (await import('@codemirror/lang-sql')).sql; - default: - throw new Error(`Unknown file type: ${fileType}`); - } -} diff --git a/packages/interactive-code-block/src/lib/components/code-output/index.tsx b/packages/interactive-code-block/src/lib/components/code-output/index.tsx deleted file mode 100644 index c29027b03f..0000000000 --- a/packages/interactive-code-block/src/lib/components/code-output/index.tsx +++ /dev/null @@ -1,268 +0,0 @@ -import { useEffect, useMemo, useState } from 'react'; -import { useHasTransitionClassName } from '../../hooks/use-has-transition-class-name'; -import { OutputFormat } from '../../types'; -// @ts-ignore -import classes from './style.module.css'; - -interface CodeOutputProps { - result?: string; - isRunning: boolean; - outputFormat?: OutputFormat; -} -export function CodeOutput({ - result, - isRunning, - outputFormat, -}: CodeOutputProps) { - const hasIsReadyClass = useHasTransitionClassName(!isRunning, 250); - const [displaySpinner, setDisplaySpinner] = useState(false); - - // Only show the spinner if the PHP is taking a while to execute the code - useEffect(() => { - if (!isRunning) { - setDisplaySpinner(false); - return; - } - - const timeout = setTimeout(() => { - setDisplaySpinner(true); - }, 20); - return () => clearTimeout(timeout); - }, [isRunning]); - - const className = [ - classes.output, - hasIsReadyClass ? classes.isReady : '', - displaySpinner ? 'is-spinner-active' : '', - ].join(' '); - - return ( -
-
Output:
- {(function () { - switch (outputFormat) { - case 'jsontabularsql': - return ( - - ); - case 'jsontabular': - return ( - - ); - case 'html': - return ( - - ); - default: - case 'plaintext': - return ( - - ); - } - })()} -
- ); -} - -function JSONTabularSQLResult({ resultString, className }: ResultsTableProps) { - const [sqlHighlighter, setSqlHighlighter] = - useState(defaultCellRenderer); - useEffect(() => { - makeSQLHighlighter().then((highlighter) => - setSqlHighlighter(() => (header: string, value: string) => { - if (header === 'query') { - return ( - - ); - } else if (header === 'params') { - if (['[]', 'null'].includes(value)) { - return ''; - } - return ( -
{JSON.stringify(JSON.parse(value), null, 2)}
- ); - } - return value; - }) - ); - }, []); - if (!sqlHighlighter) { - className = `${classes.output} is-spinner-active`; - } - return ( - - ); -} - -function makeSQLHighlighter() { - type Token = { - from: number; - to: number; - classes: string; - }; - return Promise.all([ - import('@codemirror/lang-sql'), - import('@lezer/highlight'), - import('@codemirror/language'), - ]).then(([sql, { highlightTree }, { defaultHighlightStyle }]) => { - return (query: string) => { - const parser = sql.SQLite.language.parser; - const result = parser.parse(query); - - const output = document.createElement('div'); - - function addToken({ from, to, classes }: Token) { - const span = document.createElement('SPAN'); - span.className = classes; - span.innerText = query.slice(from, to); - output.appendChild(span); - } - let lastToken: Token | null = null; - highlightTree( - result as any, - defaultHighlightStyle, - (from: number, to: number, classes: string) => { - if (lastToken && lastToken.to !== from) { - addToken({ - from: lastToken!.to, - to: from, - classes: '', - }); - } - const token = { from, to, classes }; - addToken(token); - lastToken = token; - } - ); - if (lastToken as any) { - addToken({ - from: (lastToken as any)?.to, - to: query.length, - classes: '', - }); - } - return output.outerHTML; - }; - }); -} - -type CellRenderer = (header: string, value: string) => string | JSX.Element; -interface ResultsTableProps { - resultString: string; - className: string; - cellRenderer?: CellRenderer; -} - -const INVALID_JSON = {}; - -function defaultCellRenderer(header: string, value: string) { - return value; -} -function JSONTabularResult({ - resultString, - className, - cellRenderer = defaultCellRenderer, -}: ResultsTableProps) { - const results = useMemo(() => { - try { - return JSON.parse(resultString); - } catch (e) { - return INVALID_JSON; - } - }, [resultString]); - if (results === INVALID_JSON) { - return ( - - ); - } - if (!Array.isArray(results) || !results.length) { - return {''}; - } - - const headers = Object.keys(results[0]); - - const headerHtml = ( - - {headers.map((header) => ( - {toJSONString(header)} - ))} - - ); - - const rowHtml = results.map((result: any) => ( - - {headers.map((header) => ( - {cellRenderer(header, toJSONString(result[header]))} - ))} - - )); - - return ( -
- - {headerHtml} - {rowHtml} -
-
- ); -} - -function toJSONString(value: any) { - if (typeof value === 'string') { - return value; - } - return JSON.stringify(value, null, 2); -} - -function HTMLResult({ - result, - className, -}: { - result: string; - className: string; -}) { - return ( -
- ); -} - -function PlaintextResult({ - result, - className, -}: { - result: string; - className: string; -}) { - return ( -
-
{result}
-
- ); -} diff --git a/packages/interactive-code-block/src/lib/components/code-output/loader.tsx b/packages/interactive-code-block/src/lib/components/code-output/loader.tsx deleted file mode 100644 index b4a7371e48..0000000000 --- a/packages/interactive-code-block/src/lib/components/code-output/loader.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -// @ts-ignore -import classes from './style.module.css'; -import { ProgressBar } from '../progress-bar'; - -export function CodeOutputLoader({ progress }: { progress: number }) { - return ( -
-
- Output: -
-
- -
-
- ); -} diff --git a/packages/interactive-code-block/src/lib/components/code-output/style.module.css b/packages/interactive-code-block/src/lib/components/code-output/style.module.css deleted file mode 100644 index 4d4d7f38ab..0000000000 --- a/packages/interactive-code-block/src/lib/components/code-output/style.module.css +++ /dev/null @@ -1,72 +0,0 @@ -.output { - box-sizing: border-box; - min-height: 3em; - position: relative; - overflow-y: auto; - - opacity: 1; - transition: opacity ease-in 0.25s, border-color ease-in 0.25s, - background-color ease-in 0.25s; -} - -.output.format-plaintext { - border: 1px solid #e0e0e0; -} - -.title { - margin-bottom: 5px; - font-weight: bold; -} - -.output.is-ready { - opacity: 0.5; - border-color: #f9e79f; - background-color: #f9e79f; -} - -.output.format-plaintext > pre { - margin: 15px 20px; -} - -.json-tabular { - border: 1px solid #cbcbcb; - border-spacing: 0; - border-collapse: collapse; - empty-cells: show; - min-width: 40px; - box-shadow: 0 0 20px rgba(0, 0, 0, 0.15); -} - -.json-tabular thead { - background-color: #e0e0e0; - color: #000; - text-align: left; - vertical-align: bottom; -} - -.json-tabular thead th { - color: #ffffff; - background-color: #009879; - text-transform: capitalize; - text-align: center; -} - -.json-tabular td { - background-color: initial; -} - -.json-tabular td, -.json-tabular th { - border-bottom-width: 0; - border-left: 1px solid #cbcbcb; - border-right-width: 0; - border-top-width: 0; - font-size: inherit; - margin: 0; - padding: 6px 10px; - overflow: visible; -} - -.json-tabular tr:nth-child(2n) td { - background-color: #f2f2f2; -} diff --git a/packages/interactive-code-block/src/lib/components/code-runner/index.tsx b/packages/interactive-code-block/src/lib/components/code-runner/index.tsx deleted file mode 100644 index 4cb2aff29d..0000000000 --- a/packages/interactive-code-block/src/lib/components/code-runner/index.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import * as React from 'react'; -import { useLibraries } from '../../hooks/use-libraries'; -import { ExecutionScript, CodeRunnerClass, Library } from '../../types'; -import PHPRunner from '../php-runner'; -import PlaygroundRunner from '../playground-runner'; - -export interface CodeRunnerRef { - runCode: (code: string) => any; -} - -interface InitializationOptions { - executionScript: ExecutionScript; - libraries: Library[]; - initialOutput?: string; - initialCode?: string; -} -export const CodeRunner = React.forwardRef< - CodeRunnerRef, - InitializationOptions ->(function CodeRunnerComponent( - { - executionScript, - libraries, - initialCode, - initialOutput, - }: InitializationOptions, - ref -) { - const librariesResult = useLibraries(libraries); - - if (executionScript?.runner === PHPRunner.id) { - return ( - - ); - } else if (executionScript?.runner === PlaygroundRunner.id) { - return ; - } else { - return null; - } -}); - -export const SUPPORTED_RUNNERS: CodeRunnerClass[] = [ - PHPRunner, - PlaygroundRunner, -]; - -export function getDefaultExecutionScript(id: string) { - return SUPPORTED_RUNNERS.find((r) => r.id === id)?.defaultExecutionScript; -} - -export function isDefaultScriptId(id: string) { - return !!getDefaultExecutionScript(id); -} diff --git a/packages/interactive-code-block/src/lib/components/interactive-code-snippet/default-bootstrap-script.php b/packages/interactive-code-block/src/lib/components/interactive-code-snippet/default-bootstrap-script.php deleted file mode 100644 index c6596505ac..0000000000 --- a/packages/interactive-code-block/src/lib/components/interactive-code-snippet/default-bootstrap-script.php +++ /dev/null @@ -1,3 +0,0 @@ - void; - onChange?: (updatedContents: string) => void; - onSave?: (updatedContents: string) => void; -} - -let CodeMirror: typeof CodeMirrorComponent; -const CodeMirrorPromise = import('../code-mirror').then((module) => { - CodeMirror = module.default as any; -}); - -export function InteractiveCodeSnippet({ - initialCode, - initialOutput, - libraries = [], - fileType, - executionScript, - onEval, - onChange, - onSave, -}: InteractiveCodeSnippetProps) { - const [computedResult, setComputedResult] = useState(initialOutput); - const result = computedResult ?? initialOutput; - useEffect(() => { - if (result) { - onEval?.(result); - } - }, [result]); - - usePromise(CodeMirrorPromise); - const editorRef = useRef(null); - const runnerRef = useRef(null); - async function run() { - const code = editorRef.current - ? editorRef.current!.getContents() - : initialCode; - const result = await runnerRef.current!.runCode(code); - setComputedResult(result); - } - function handleSave() { - run(); - const code = editorRef.current - ? editorRef.current.getContents() - : initialCode; - onSave?.(code); - } - - return ( -
-
-
- Live Example: - -
- {CodeMirror ? ( - - ) : ( - - )} -
- -
- ); -} diff --git a/packages/interactive-code-block/src/lib/components/interactive-code-snippet/style.module.css b/packages/interactive-code-block/src/lib/components/interactive-code-snippet/style.module.css deleted file mode 100644 index bb7129b911..0000000000 --- a/packages/interactive-code-block/src/lib/components/interactive-code-snippet/style.module.css +++ /dev/null @@ -1,31 +0,0 @@ -.snippet { - display: flex; - flex-direction: column; - gap: 1rem; -} - -:local .snippet :global .cm-editor { - border: 1px solid #e0e0e0; -} - -.snippet code { - display: block; -} - -.title { - display: flex; - gap: 1rem; - align-items: center; - flex-direction: row; - margin-bottom: 5px; -} - -.run-button { - font-size: 18px; - padding: 10px !important; - padding-right: 15px !important; -} - -:local .run-button :global .dashicon { - margin-right: 5px !important; -} diff --git a/packages/interactive-code-block/src/lib/components/libraries-control/index.tsx b/packages/interactive-code-block/src/lib/components/libraries-control/index.tsx deleted file mode 100644 index 0953822a67..0000000000 --- a/packages/interactive-code-block/src/lib/components/libraries-control/index.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import * as React from '@wordpress/element'; -import { useEntityRecords } from '@wordpress/core-data'; -import { ToggleControl, Spinner, Flex, FlexItem } from '@wordpress/components'; -import { Library } from '../../types'; - -interface LibrariesControlProps { - selected: string[]; - librariesIncludedByScript?: string[]; - onChange: (selected: string[]) => void; -} - -export default function LibrariesControl({ - selected, - librariesIncludedByScript, - onChange, -}: LibrariesControlProps) { - const libraries = useEntityRecords( - 'interactive-code-block', - 'library' - ); - - if (libraries.records === null) { - return ; - } - - if (!selected) { - selected = []; - } - - function toggleLibrary(id: string) { - if (selected.includes(id)) { - onChange(selected.filter((selectedId) => selectedId !== id)); - } else { - onChange([...selected, id]); - } - } - - return ( - - {libraries.records.map((library) => { - const isIncludedByScript = librariesIncludedByScript?.includes( - library.id - ); - if (isIncludedByScript) { - return ( - - - - ); - } - return ( - - toggleLibrary(library.id)} - checked={selected.includes(library.id)} - /> - - ); - })} - - ); -} diff --git a/packages/interactive-code-block/src/lib/components/php-runner/index.tsx b/packages/interactive-code-block/src/lib/components/php-runner/index.tsx deleted file mode 100644 index 6a1b7e83aa..0000000000 --- a/packages/interactive-code-block/src/lib/components/php-runner/index.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import * as React from 'react'; -import { PHPClient } from '../../php-worker'; -import { ExecutionScript } from '../../types'; -import { CodeOutput } from '../code-output'; -import { CodeOutputLoader } from '../code-output/loader'; -import { UseLibrariesResult } from '../../hooks/use-libraries'; -import sharedPHPLoader from './php-loader'; -import classnames from 'classnames'; - -interface State { - userScriptPath: string; - executionScriptPath: string; - isReady: boolean; - isRunning: boolean; - result?: string; - lastEnqueuedCode?: string; - phpProgress: number; -} -interface Props { - executionScript?: ExecutionScript; - initialOutput?: string; - libraries: UseLibrariesResult; -} - -export default class PHPRunner extends React.Component { - static id = 'PHP' as const; - static defaultExecutionScript: ExecutionScript = { - id: PHPRunner.id, - runner: PHPRunner.id, - name: 'PHP', - content: ` - ) => { - this.setState({ - phpProgress: e.detail, - }); - }) as any); - this.php - .then((php) => php.isReady()) - .then(() => { - this.updateLoadedLibraries(); - this.setState({ isReady: true }); - if (this.state.lastEnqueuedCode) { - this.runCode(this.state.lastEnqueuedCode); - this.setState({ - lastEnqueuedCode: undefined, - }); - } - }); - } - - get php(): Promise { - return sharedPHPLoader.load(); - } - - async updateLoadedLibraries() { - const php = await this.php; - const loaded = this.props.libraries.loaded; - for (const name in loaded) { - php!.writeFile(`/${name}`, new Uint8Array(loaded[name])); - } - } - - async runCode(code: string) { - this.setState({ isRunning: true }); - - if (!this.state.isReady) { - this.setState({ lastEnqueuedCode: code }); - return ''; - } - - const php = await this.php; - php.writeFile( - this.state.executionScriptPath, - `` + - this.props.executionScript!.content - ); - php.writeFile(this.state.userScriptPath, code); - const { text } = await php.run({ - scriptPath: this.state.executionScriptPath, - }); - this.setState({ - result: text, - isRunning: false, - }); - return text; - } - - override render() { - if (!this.state.isReady) { - return ; - } - - const className = classnames('has-interactive-code-spinner', { - ['is-spinner-active']: this.state.isRunning, - }); - - return ( -
- -
- ); - } - - get progress() { - const parts = [ - this.props.libraries.progress, - this.state.phpProgress, - ].filter((p) => p !== undefined) as number[]; - return parts.reduce((a, b) => a + b, 0) / parts.length; - } -} diff --git a/packages/interactive-code-block/src/lib/components/php-runner/php-loader.ts b/packages/interactive-code-block/src/lib/components/php-runner/php-loader.ts deleted file mode 100644 index 7747b7849e..0000000000 --- a/packages/interactive-code-block/src/lib/components/php-runner/php-loader.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { LoadingStatus } from '../../types'; -import { - consumeAPI, - recommendedWorkerBackend, - spawnPHPWorkerThread, -} from '@php-wasm/web'; - -import type { PHPClient } from '../../php-worker'; - -export class PHPLoader extends EventTarget { - private promise?: Promise; - status: LoadingStatus = 'idle'; - progress = 0; - - load() { - if (!this.promise) { - this.promise = this._load(); - } - return this.promise!; - } - - async _load() { - this.status = 'loading'; - const { default: workerScriptUrl } = await import( - /** @ts-ignore */ - '../../php-worker.ts?url&worker' - ); - const worker = await spawnPHPWorkerThread( - workerScriptUrl, - recommendedWorkerBackend - ); - const php = consumeAPI(worker); - php?.onDownloadProgress((e) => { - const { loaded, total } = e.detail; - this.progress = (100 * loaded) / total; - this.dispatchEvent( - new CustomEvent('progress', { - detail: this.progress, - }) - ); - }); - await php?.isReady(); - this.status = 'ready'; - return php; - } -} - -const sharedPHPLoader: PHPLoader = new PHPLoader(); -export default sharedPHPLoader; diff --git a/packages/interactive-code-block/src/lib/components/playground-runner/index.tsx b/packages/interactive-code-block/src/lib/components/playground-runner/index.tsx deleted file mode 100644 index cd9080250c..0000000000 --- a/packages/interactive-code-block/src/lib/components/playground-runner/index.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import * as React from 'react'; -import * as Comlink from 'comlink'; -import { ExecutionScript } from '../../types'; -// @ts-ignore -import { default as playgroundUrl } from '../../../../public/playground-sandbox.html?url'; -import classnames from 'classnames'; -import ReactDOM from 'react-dom'; -const sandboxHash = playgroundUrl.split('-').pop().split('.')[0]; - -interface SandboxApi { - isReady(): Promise; - execute(code: string): Promise; -} - -interface Props { - initialCode?: string; - executionScript?: ExecutionScript; -} -interface State { - iframeId: string; - isReady: boolean; - iframeLoaded: boolean; - isLoading: boolean; - isRunning: boolean; - hasOwnLoader: boolean; - - lastEnqueuedCode?: string; - client?: SandboxApi; -} - -export default class PlaygroundRunner extends React.Component { - static id = 'WordPress Playground' as const; - static defaultExecutionScript: ExecutionScript = { - id: PlaygroundRunner.id, - runner: PlaygroundRunner.id, - name: 'WordPress Playground', - content: `/* Implemented in TypeScript as PlaygroundRunner.runCode() */`, - }; - - constructor(props: Props) { - if (!props.executionScript) { - props.executionScript = PlaygroundRunner.defaultExecutionScript; - } - super(props); - this.state = { - iframeId: 'playground-frame', - iframeLoaded: false, - isReady: true, - isLoading: true, - isRunning: false, - hasOwnLoader: true, - lastEnqueuedCode: props.initialCode, - }; - } - - async onIframeLoaded() { - if (!this.state.client) { - const root = ReactDOM.findDOMNode(this)! as Element; - const iframe = root.querySelector('iframe')!; - const endpoint = Comlink.windowEndpoint(iframe.contentWindow!); - const client = Comlink.wrap(endpoint); - this.setState({ - client, - }); - await client.isReady(); - - /** - * Run the initial (or last requested) code snippet once - * the playground site is loaded - */ - if (this.state.lastEnqueuedCode) { - this.runCode(this.state.lastEnqueuedCode); - this.setState({ lastEnqueuedCode: undefined }); - } - - this.setState({ - isLoading: false, - }); - } - } - - async runCode(code: string) { - if (this.state.isRunning) { - this.setState({ lastEnqueuedCode: code }); - return; - } - - this.setState({ isRunning: true }); - - if (!this.state.client) { - this.setState({ lastEnqueuedCode: code }); - return; - } - - await this.state.client.isReady(); - await this.state.client.execute(code); - - this.setState({ isRunning: false }); - - if (this.state.lastEnqueuedCode) { - const newCode = this.state.lastEnqueuedCode; - this.setState({ lastEnqueuedCode: undefined }); - await this.runCode(newCode); - } - } - - override render() { - const className = classnames('has-interactive-code-spinner', { - ['is-spinner-active']: this.state.isLoading, - }); - // @TODO remove allow-scripts for true sandboxing - return ( -
- -
- ); - } -} diff --git a/packages/interactive-code-block/src/lib/components/progress-bar/index.tsx b/packages/interactive-code-block/src/lib/components/progress-bar/index.tsx deleted file mode 100644 index 3c19fac2b7..0000000000 --- a/packages/interactive-code-block/src/lib/components/progress-bar/index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -// @ts-ignore -import classes from './style.module.css'; - -export function ProgressBar({ progress }: { progress: number }) { - return ( -
- Loading PHP... -
-
-
-
- ); -} diff --git a/packages/interactive-code-block/src/lib/components/progress-bar/style.module.css b/packages/interactive-code-block/src/lib/components/progress-bar/style.module.css deleted file mode 100644 index 0dba6531ed..0000000000 --- a/packages/interactive-code-block/src/lib/components/progress-bar/style.module.css +++ /dev/null @@ -1,35 +0,0 @@ -.root { - display: flex; - flex-direction: column; - position: absolute; - top: 0; - left: 0; - z-index: 5; - display: flex; - width: 100%; - height: 100%; - justify-content: center; - align-items: center; - pointer-events: none; -} - -.wrapper { - position: relative; - width: 80%; - height: 4px; - margin: 4px auto; - border-radius: 10px; - background: #e0e0e0; -} - -.bar { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 100%; - width: 0; - background: #3858e9; - border-radius: 2px; - transition: opacity linear 0.25s, width ease-in 0.5s; -} diff --git a/packages/interactive-code-block/src/lib/components/toolbar-dropdown/index.tsx b/packages/interactive-code-block/src/lib/components/toolbar-dropdown/index.tsx deleted file mode 100644 index e75476b797..0000000000 --- a/packages/interactive-code-block/src/lib/components/toolbar-dropdown/index.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import * as React from '@wordpress/element'; -import classnames from 'classnames'; -import { - Button, - Dropdown, - MenuGroup, - MenuItem, - NavigableMenu, -} from '@wordpress/components'; - -interface ToolbarDropdownProps { - options: { - label: string; - value: string; - }[]; - optionsLabel?: string; - icon?: { - icon: string; - }; - value: string; - onChange: (value: string) => void; -} - -/** - * @typedef {Object} DropdownOption - * - * @property {string} label Option label. - * @property {string} value Option value. - */ -/** - * Dropdown for the editor toolbar. - * - * @param {Object} props Component props. - * @param {DropdownOption[]} props.options Dropdown options. - * @param {string} [props.optionsLabel] Options label. - * @param {Object} [props.icon] Icon for the toolbar. - * @param {string} props.value Current dropdown value. - * @param {Function} props.onChange Dropdown change callback, which receive the new value as argument. - * - * @return {Object} React component. - */ -const ToolbarDropdown = ({ - options, - optionsLabel, - icon, - value, - onChange, - ...props -}: ToolbarDropdownProps) => { - const selectedOption = options.find((option) => value === option.value); - - return ( - ( - - {isOpen && ( - { - if (e.target.closest('.cm-editor')) { - return; - } - closeModal(); - }} - title="Create a new script" - > - { - closeModal(); - }} - /> - - )} - - ); -} - -export function CreateScriptForm({ onSaveFinished }) { - const { createErrorNotice, createSuccessNotice } = useDispatch( - noticesStore - ) as any; - const [script, setScript] = useState({ - id: '', - name: '', - runner: PHPRunner.id, - content: '', - }); - const { lastError, isSaving } = useSelect( - (select) => ({ - lastError: select(coreDataStore).getLastEntitySaveError( - 'interactive-code-block', - 'script', - undefined! - ), - isSaving: select(coreDataStore).isSavingEntityRecord( - 'interactive-code-block', - 'script', - undefined! - ), - }), - [] - ); - - const { saveEntityRecord } = useDispatch(coreDataStore); - const handleSave = async () => { - try { - await saveEntityRecord('interactive-code-block', 'script', script, { - throwOnError: true, - }); - onSaveFinished(); - createSuccessNotice('Script created successfully', { - type: 'snackbar', - }); - } catch (error) { - createErrorNotice(error.message, { - type: 'snackbar', - }); - } - }; - - return ( - setScript({ ...script, ...fields })} - canSave={!!(script.name && script.content)} - onSave={handleSave} - lastError={lastError} - isSaving={isSaving} - /> - ); -} - -export function EditScriptButton({ id }) { - const [isOpen, setOpen] = useState(false); - const openModal = () => setOpen(true); - const closeModal = () => setOpen(false); - return ( - <> - - {isOpen && ( - - - - )} - - ); -} - -export function EditScriptForm({ id, onSaveFinished }) { - const { createErrorNotice, createSuccessNotice } = useDispatch( - noticesStore - ) as any; - const { script, lastError, isSaving, hasEdits } = useSelect( - (select) => ({ - script: select(coreDataStore).getEditedEntityRecord( - 'interactive-code-block', - 'script', - id - ) as any as ExecutionScript, - lastError: select(coreDataStore).getLastEntitySaveError( - 'interactive-code-block', - 'script', - id - ), - isSaving: select(coreDataStore).isSavingEntityRecord( - 'interactive-code-block', - 'script', - id - ), - hasEdits: select(coreDataStore).hasEditsForEntityRecord( - 'interactive-code-block', - 'script', - id - ), - }), - [id] - ); - - const { saveEditedEntityRecord, editEntityRecord } = - useDispatch(coreDataStore); - const handleSave = async () => { - try { - await saveEditedEntityRecord( - 'interactive-code-block', - 'script', - id, - { - throwOnError: true, - } - ); - createSuccessNotice('Script saved successfully', { - type: 'snackbar', - }); - onSaveFinished(); - } catch (error) { - createErrorNotice(error.message, { - type: 'snackbar', - }); - } - }; - const handleChange = (delta) => - editEntityRecord('interactive-code-block', 'script', id, delta); - - if (isDefaultScriptId(id)) { - return ( - - ); - } - - return ( - - ); -} - -interface ScriptFormProps { - script: ExecutionScript; - onChange: (delta: Partial) => void; - canSave: boolean; - lastError?: Error; - isSaving?: boolean; - onSave?: () => void; -} -function ScriptForm({ - script, - onChange, - canSave, - lastError, - isSaving, - onSave, -}: ScriptFormProps) { - const initialContents = useMemo(() => script.content, []); - const editorRef = useRef(null); - function handleSave(e) { - e.preventDefault(); - onSave?.(); - } - return ( -
- {lastError ? ( -
Error: {lastError.message}
- ) : ( - false - )} - - - - onChange({ name: newName })} - autoFocus - /> - - - - ({ - label: id, - value: id, - }))} - onChange={(newRunner) => - onChange({ runner: newRunner }) - } - /> - - - - ({ - label, - value, - }) - )} - onChange={(newOutputFormat) => - onChange({ outputFormat: newOutputFormat }) - } - /> - - - - - onChange({ content })} - onSave={(content) => onChange({ content })} - ref={editorRef} - /> - - - - {isDefaultScriptId(script.id) ? ( - false - ) : ( - - - - onChange({ libraries }) - } - selected={script.libraries || []} - /> - - - )} - - - {isDefaultScriptId(script.id) ? ( - "This is the default execution script and can't be changed." - ) : ( -
- -
- )} -
-
-
- ); -} diff --git a/packages/interactive-code-block/src/lib/pages/execution-scripts-list.tsx b/packages/interactive-code-block/src/lib/pages/execution-scripts-list.tsx deleted file mode 100644 index 642a7fc800..0000000000 --- a/packages/interactive-code-block/src/lib/pages/execution-scripts-list.tsx +++ /dev/null @@ -1,160 +0,0 @@ -/** - * WordPress dependencies - */ -import * as React from '@wordpress/element'; -import { Button, Flex, FlexItem, Spinner } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; -import { store as noticesStore } from '@wordpress/notices'; -import { useDispatch } from '@wordpress/data'; -import { useEntityRecords, useEntityRecord } from '@wordpress/core-data'; - -/** - * Internal dependencies - */ -import { - CreateScriptButton, - EditScriptButton, -} from './execution-scripts-forms'; -import { ExecutionScript } from '../types'; -import { - SUPPORTED_RUNNERS, - isDefaultScriptId, - getDefaultExecutionScript, -} from '../components/code-runner'; - -export default function ExecutionScriptsPanel() { - return ( - - -

- When the user clicks "Run" in an interactive code block, - they don't actually run the code snippet. Instead, - they run an execution script associated with the code - block. -

- -

- The built-in execution scripts are quite simple, but you're - free to get more fancy and: -

- -
    -
  • Load a few libraries
  • -
  • Expose some variables to the code snippet
  • -
  • Preload an SQLite database
  • -
  • Highlight the code snippet instead of executing it
  • -
-
- - - - - - -
- ); -} - -// List of scripts -function ScriptsList() { - const scripts = useEntityRecords( - 'interactive-code-block', - 'script' - ); - - if (scripts.records === null) { - return ( - - - - - - ); - } - - if (!scripts.records?.length && !SUPPORTED_RUNNERS.length) { - return
No results
; - } - - return ( -
    - {SUPPORTED_RUNNERS.map((runnerClass) => ( - - ))} - {scripts.records.map(({ id }) => ( - - ))} -
- ); -} - -const ScriptsListItem = ({ id }) => { - const { createSuccessNotice, createErrorNotice } = useDispatch( - noticesStore - ) as any; - const script = useEntityRecord( - 'interactive-code-block', - 'script', - id - ); - if (isDefaultScriptId(id)) { - script.editedRecord = getDefaultExecutionScript(id)!; - script.editedRecord = { - ...script.editedRecord, - name: `${script.editedRecord.name} (built-in)`, - }; - } - const { deleteEntityRecord } = useDispatch('core'); - - const handleDelete = async () => { - // eslint-disable-next-line no-alert - const confirmation = window.confirm( - __( - 'Are you sure you want to delete this script? ALL code blocks using this script will stop working.' - ) - ); - if (!confirmation) { - return; - } - try { - await deleteEntityRecord( - 'interactive-code-block', - 'script', - id, - null, - { - throwOnError: true, - } - ); - // Tell the user the operation succeeded: - createSuccessNotice('The script was deleted!', { - type: 'snackbar', - }); - } catch (e) { - const message = - (e?.message || 'There was an error.') + - ' Please refresh the page and try again.'; - // Tell the user how exactly the operation has failed: - createErrorNotice(message, { - type: 'snackbar', - }); - } - }; - - const AnyItem = FlexItem as any; - return ( - - {script.editedRecord.name} - - - - - {!isDefaultScriptId(id) && ( - - )} - - - ); -}; diff --git a/packages/interactive-code-block/src/lib/pages/execution-scripts-page.tsx b/packages/interactive-code-block/src/lib/pages/execution-scripts-page.tsx deleted file mode 100644 index 618f63bfdd..0000000000 --- a/packages/interactive-code-block/src/lib/pages/execution-scripts-page.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import * as React from '@wordpress/element'; -import { render } from '@wordpress/element'; - -import ExecutionScriptsList from './execution-scripts-list'; -import { installEntities } from './entities'; -import Notifications from './notifications'; -installEntities(); - -window.addEventListener( - 'load', - function () { - render( - <> - - - , - document.querySelector('#execution-scripts') - ); - }, - false -); diff --git a/packages/interactive-code-block/src/lib/pages/libraries-list.tsx b/packages/interactive-code-block/src/lib/pages/libraries-list.tsx deleted file mode 100644 index 98ffc5dbd8..0000000000 --- a/packages/interactive-code-block/src/lib/pages/libraries-list.tsx +++ /dev/null @@ -1,192 +0,0 @@ -import * as React from '@wordpress/element'; - -/** - * WordPress dependencies - */ -import { Button, Flex, FlexItem, Spinner } from '@wordpress/components'; -import apiFetch from '@wordpress/api-fetch'; -import { __ } from '@wordpress/i18n'; -import { store as noticesStore } from '@wordpress/notices'; -import { useState } from '@wordpress/element'; -import { useDispatch } from '@wordpress/data'; -import { - useEntityRecords, - useEntityRecord, - store as coreDataStore, -} from '@wordpress/core-data'; - -/** - * Internal dependencies - */ -import UploadOverlay from './upload-overlay'; -import { useOnEscapeKey } from '../hooks/use-on-escape-key'; -import { Library } from '../types'; - -export default function PharLibrariesPanel() { - const { createErrorNotice, createSuccessNotice } = useDispatch( - noticesStore - ) as any; - const { saveEntityRecord } = useDispatch(coreDataStore); - const [isUploading, setIsUploading] = useState(false); - - const handleUpload = async (file) => { - setIsUploading(true); - - const formData = new FormData(); - formData.append('file', file); - try { - await saveEntityRecord( - 'interactive-code-block', - 'library', - { - name: file.name, - }, - { - throwOnError: true, - __unstableFetch: ({ data, ...options }) => { - for (const key in data) { - formData.append(key, data[key]); - } - const headers = { ...options.headers }; - delete headers['Content-Type']; - return apiFetch({ - ...options, - headers, - body: formData, - }); - }, - } - ); - - createSuccessNotice(`Library uploaded successfully`, { - type: 'snackbar', - }); - } catch (error) { - createErrorNotice(`Error creating a library: ${error.message}`, { - type: 'snackbar', - }); - } finally { - setIsUploading(false); - } - }; - - return ( - - - Upload libraries (e.g. .phar files) to use in your interative - code blocks. Uploading a library with the same name twice - overwrites the previous version. - - - - - - - - - ); -} - -// List of libraries -function LibraryList() { - const libraries = useEntityRecords( - 'interactive-code-block', - 'library' - ); - - if (libraries.records === null) { - return ( - - - - - - ); - } - - if (!libraries.records?.length) { - return
No results
; - } - - return ( -
    - {libraries.records.map(({ id }) => ( - - ))} -
- ); -} - -const LibraryListItem = ({ id }) => { - const { createSuccessNotice, createErrorNotice } = useDispatch( - noticesStore - ) as any; - const library = useEntityRecord( - 'interactive-code-block', - 'library', - id - ); - const { deleteEntityRecord } = useDispatch('core'); - - const [isEditing, setIsEditing] = useState(false); - useOnEscapeKey(function () { - if (isEditing) { - library.edit({ name: library.record!.name }); - setIsEditing(false); - } - }); - - const handleDelete = async () => { - // eslint-disable-next-line no-alert - const confirmation = window.confirm( - __( - 'Are you sure you want to delete this library? ALL code blocks using this library will stop working.' - ) - ); - if (!confirmation) { - return; - } - try { - await deleteEntityRecord( - 'interactive-code-block', - 'library', - id, - null, - { throwOnError: true } - ); - // Tell the user the operation succeeded: - createSuccessNotice('The library was deleted!', { - type: 'snackbar', - }); - } catch (e) { - const message = - (e?.message || 'There was an error.') + - ' Please refresh the page and try again.'; - // Tell the user how exactly the operation has failed: - createErrorNotice(message, { - type: 'snackbar', - }); - } - }; - - const AnyItem = FlexItem as any; - return ( - - setIsEditing(true)}> - {library.editedRecord.name} - - - - - - ); -}; diff --git a/packages/interactive-code-block/src/lib/pages/libraries-page.tsx b/packages/interactive-code-block/src/lib/pages/libraries-page.tsx deleted file mode 100644 index b7ec1daea6..0000000000 --- a/packages/interactive-code-block/src/lib/pages/libraries-page.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import * as React from '@wordpress/element'; - -import { render } from '@wordpress/element'; -import PharLibrariesList from './libraries-list'; -import { installEntities } from './entities'; -import Notifications from './notifications'; -installEntities(); - -window.addEventListener( - 'load', - function () { - render( - <> - - - , - document.querySelector('#libraries') - ); - }, - false -); diff --git a/packages/interactive-code-block/src/lib/pages/notifications.tsx b/packages/interactive-code-block/src/lib/pages/notifications.tsx deleted file mode 100644 index 247a212635..0000000000 --- a/packages/interactive-code-block/src/lib/pages/notifications.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import * as React from '@wordpress/element'; - -import { SnackbarList } from '@wordpress/components'; -import { useSelect, useDispatch } from '@wordpress/data'; -import { store as noticesStore } from '@wordpress/notices'; - -export default function Notifications() { - const notices = useSelect( - (select) => (select(noticesStore) as any).getNotices(), - [] - ); - const { removeNotice } = useDispatch(noticesStore); - const snackbarNotices = notices.filter(({ type }) => type === 'snackbar'); - return ( - - ); -} diff --git a/packages/interactive-code-block/src/lib/pages/upload-overlay.tsx b/packages/interactive-code-block/src/lib/pages/upload-overlay.tsx deleted file mode 100644 index 47b04ea65f..0000000000 --- a/packages/interactive-code-block/src/lib/pages/upload-overlay.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import * as React from '@wordpress/element'; -import { useRef } from '@wordpress/element'; - -const UploadOverlay = ({ onFileSelected }) => { - const fileInputRef = useRef(null); - - const handleFileChange = async (event) => { - await onFileSelected(event.target.files[0]); - event.target.value = null; - }; - - return ( - - ); -}; - -export default UploadOverlay; diff --git a/packages/interactive-code-block/src/lib/php-worker.ts b/packages/interactive-code-block/src/lib/php-worker.ts deleted file mode 100644 index ff61709554..0000000000 --- a/packages/interactive-code-block/src/lib/php-worker.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { - exposeAPI, - parseWorkerStartupOptions, - PublicAPI, - WebPHP, - WebPHPEndpoint, -} from '@php-wasm/web'; -import { EmscriptenDownloadMonitor } from '@php-wasm/progress'; -import { - LatestSupportedPHPVersion, - SupportedPHPVersion, - SupportedPHPVersionsList, -} from '@php-wasm/universal'; - -interface WorkerOptions extends Record { - php: string; -} -const options = parseWorkerStartupOptions(); -const phpVersion = (options?.php?.replace('_', '.') || - LatestSupportedPHPVersion) as SupportedPHPVersion; -if (!SupportedPHPVersionsList.includes(phpVersion)) { - throw new Error(`Unsupported PHP version ${phpVersion}`); -} - -const monitor = new EmscriptenDownloadMonitor(); -const { php, phpReady } = WebPHP.loadSync(phpVersion, { - requestHandler: { - documentRoot: '/', - absoluteUrl: 'https://example.com/', - }, - downloadMonitor: monitor, -}); - -const [setApiReady, client] = exposeAPI(new WebPHPEndpoint(php, monitor)); -await phpReady; - -setApiReady(); - -export type PHPClient = PublicAPI; -export const assertClientType: PHPClient = client; diff --git a/packages/interactive-code-block/src/lib/types.ts b/packages/interactive-code-block/src/lib/types.ts deleted file mode 100644 index 88266d308e..0000000000 --- a/packages/interactive-code-block/src/lib/types.ts +++ /dev/null @@ -1,67 +0,0 @@ -export type MemFile = { - fileName: string; - contents: string; -}; - -export const outputFormats = { - plaintext: 'Plain text', - html: 'HTML', - jsontabular: 'JSON (tabular)', - jsontabularsql: 'JSON (tabular, SQL queries)', -} as const; -export type OutputFormat = keyof typeof outputFormats; - -export interface ExecutionScript { - id: string; - runner: CodeRunnerClass['id']; - name?: string; - content: string; - outputFormat?: OutputFormat; - libraries?: string[]; -} - -export interface Library { - id: string; - name: string; - url: string; -} - -export type LoadingStatus = 'idle' | 'loading' | 'ready' | 'error'; - -export interface InteractiveCodeSnippetBlockAttributes { - code: string; - fileType: 'php' | 'sql'; - executionScript: string; - libraries: string[]; - cachedOutput: string; - showCachedOutput: boolean; -} - -export interface ICodeRunner { - isReady: boolean; - isRunning: boolean; - result?: string; - run(code: string): Promise; - addEventListener(event: string, listener: any): void; - setLoadedLibraries(loadedLibraries: Record): void; -} - -import type PHPRunner from './components/php-runner'; -import type PlaygroundRunner from './components/playground-runner'; - -export type CodeRunner = PHPRunner | PlaygroundRunner; - -// Get class type from instance type -export interface Type extends Function { - new (...args: any[]): T; -} -export type CodeRunnerClass = Type & { - id: string; - defaultExecutionScript: ExecutionScript; -}; - -export interface Loader { - load(): Promise; - // progress or ready - addEventListener(event: string, callback: any): void; -} diff --git a/packages/interactive-code-block/src/lib/utils.ts b/packages/interactive-code-block/src/lib/utils.ts deleted file mode 100644 index 50cd5a3534..0000000000 --- a/packages/interactive-code-block/src/lib/utils.ts +++ /dev/null @@ -1,5 +0,0 @@ -export function setFileName(url: URL, newFilename: any) { - const currentScriptPath = url.pathname.split('/').slice(0, -1).join('/'); - url.pathname = `${currentScriptPath}/${newFilename}`; - return url; -} diff --git a/packages/interactive-code-block/tsconfig.json b/packages/interactive-code-block/tsconfig.json deleted file mode 100644 index 368f1cc019..0000000000 --- a/packages/interactive-code-block/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "forceConsistentCasingInFileNames": true, - "strict": true, - "jsx": "react-jsx", - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "allowSyntheticDefaultImports": true, - "types": ["vitest", "react"] - }, - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.lib.json" - }, - { - "path": "./tsconfig.spec.json" - } - ] -} diff --git a/packages/interactive-code-block/tsconfig.lib.json b/packages/interactive-code-block/tsconfig.lib.json deleted file mode 100644 index 03cb63a509..0000000000 --- a/packages/interactive-code-block/tsconfig.lib.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../dist/out-tsc", - "declaration": true - }, - "include": [ - "src/**/*.ts", - "src/lib/block/view-async.tsx", - "src/lib/block/register.tsx", - "src/lib/components/php-runner/index.tsx", - "src/lib/components/playground-runner/index.tsx", - "src/lib/hooks/use-code-runner.tsx", - "src/lib/hooks/use-libraries.tsx", - "src/lib/components/code-runner/index.tsx" - ], - "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] -} diff --git a/packages/interactive-code-block/tsconfig.spec.json b/packages/interactive-code-block/tsconfig.spec.json deleted file mode 100644 index eb23daacbc..0000000000 --- a/packages/interactive-code-block/tsconfig.spec.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../dist/out-tsc", - "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"] - }, - "include": [ - "vite.config.ts", - "src/**/*.test.ts", - "src/**/*.spec.ts", - "src/**/*.test.tsx", - "src/**/*.spec.tsx", - "src/**/*.test.js", - "src/**/*.spec.js", - "src/**/*.test.jsx", - "src/**/*.spec.jsx", - "src/**/*.d.ts" - ] -} diff --git a/packages/interactive-code-block/vite.config.ts b/packages/interactive-code-block/vite.config.ts deleted file mode 100644 index a6cb9e2aa6..0000000000 --- a/packages/interactive-code-block/vite.config.ts +++ /dev/null @@ -1,264 +0,0 @@ -/// -import { defineConfig } from 'vite'; -import * as fs from 'fs'; -import * as crypto from 'crypto'; -import react from '@vitejs/plugin-react'; -import externalGlobals from 'rollup-plugin-external-globals'; -import { viteStaticCopy } from 'vite-plugin-static-copy'; -import type { OutputOptions } from 'rollup'; - -// eslint-disable-next-line @nx/enforce-module-boundaries -import { viteTsConfigPaths } from '../vite-ts-config-paths'; - -const path = (filename: string) => new URL(filename, import.meta.url).pathname; -export default defineConfig({ - base: './', - assetsInclude: ['**/*.wasm', '**/*.data'], - cacheDir: '../../node_modules/.vite/interactive-code-block', - - css: { - modules: { - localsConvention: 'camelCaseOnly', - }, - }, - - plugins: [ - viteTsConfigPaths({ - root: '../../', - }), - - viteStaticCopy({ - targets: [ - { - src: path('../../dist/packages/playground/client/index.js'), - dest: 'assets/', - rename: () => 'playground-client.js', - }, - { - src: new URL('src/lib/block/editor.css', import.meta.url) - .pathname, - dest: 'assets/', - }, - { - src: new URL('src/lib/block/view.css', import.meta.url) - .pathname, - dest: 'assets/', - }, - ], - }), - - ...WordPressVitePlugins({ - manifest: { - entrypoints: [ - 'editor', - 'view', - 'execution-scripts', - 'libraries', - ], - }, - }), - ], - - // Uncomment this if you are using workers. - worker: { - format: 'es', - plugins: [ - viteTsConfigPaths({ - root: '../../', - }), - ], - }, - - // Configuration for building your library. - // See: https://vitejs.dev/guide/build.html#library-mode - build: { - assetsInlineLimit: 0, - minify: true, - rollupOptions: { - preserveEntrySignatures: 'strict', - plugins: [react()], - input: { - ['comlink']: 'comlink/dist/esm/comlink.mjs', - ['editor']: path('src/lib/block/editor.tsx'), - ['view']: path('src/lib/block/view.ts'), - ['execution-scripts']: path( - 'src/lib/pages/execution-scripts-page.tsx' - ), - ['libraries']: path('src/lib/pages/libraries-page.tsx'), - }, - output: { - // Change this to the formats you want to support. - // Don't forgot to update your package.json as well. - format: 'es', - entryFileNames: (entryInfo) => { - if (entryInfo.name.includes('comlink')) { - return 'assets/comlink.js'; - } - return 'assets/[name]-[hash].js'; - }, - chunkFileNames: (assetInfo) => { - if (assetInfo.name === 'view.ts') { - return 'assets/view.js'; - } - return 'assets/[name]-[hash].js'; - }, - assetFileNames: (assetInfo) => { - if (assetInfo.name === 'block.json') { - return 'assets/block.json'; - } - return 'assets/[name]-[hash][extname]'; - }, - }, - }, - }, - - test: { - globals: true, - cache: { - dir: '../../node_modules/.vitest', - }, - environment: 'jsdom', - include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], - }, -}); - -interface WordPressVitePluginOptions { - manifest?: { - entrypoints?: string[]; - }; -} -function WordPressVitePlugins(pluginOptions: WordPressVitePluginOptions): any { - const wpImports: string[] = []; - return [ - externalGlobals( - ((id: string) => { - const globalVar = getGlobalForModule(id); - if (globalVar) { - if (id.includes('@wordpress/')) { - wpImports.push(globalVar); - } - return globalVar; - } - return; - }) as any, - { - include: ['**/*.ts', '**/*.js', '**/*.tsx', '**/*.jsx'], - } - ), - { - name: 'wordpress-dependency-extraction', - enforce: 'pre', - generateBundle( - options: OutputOptions, - bundle: Record - ) { - const deps = [...new Set(wpImports)] - // wp.element to wp-element - .map((dep) => dep.replace('.', '-')) - // wp-blockEditor to wp-block-editor - .map((dep) => - dep.replace( - /[A-Z]/g, - (letter) => `-${letter.toLowerCase()}` - ) - ); - - const files = []; - let code = ''; - for (const [, value] of Object.entries(bundle)) { - code += value.code; - const include = - !value['name'].endsWith('.wasm') && - (!pluginOptions?.manifest?.entrypoints || - pluginOptions.manifest.entrypoints?.includes( - value['name'] - )); - if (include) { - files.push( - `${JSON.stringify( - value['name'] - )} => ${JSON.stringify(value['fileName'])},` - ); - } - } - fs.writeFileSync( - `${options.dir}/index.asset.php`, - ` ${JSON.stringify(sha256(code).substring(0, 8))}, - 'dependencies' => ${JSON.stringify(deps)}, - 'files' => array( - ${files.join('\n')} - ) - ); - ` - ); - }, - }, - ]; -} - -function sha256(buffer: any) { - const hash = crypto.createHash('sha256'); - hash.update(buffer); - return hash.digest('hex'); -} - -function getGlobalForModule(id: string) { - return { - react: 'window.wp.element', - 'react-dom': 'window.wp.element', - lodash: 'window.lodash', - 'lodash-es': 'window.lodash', - moment: 'window.moment', - jquery: 'window.jQuery', - tinymce: 'tinymce', - backbone: 'Backbone', - '@wordpress/a11y': 'wp.a11y', - '@wordpress/api-fetch': 'wp.apiFetch', - '@wordpress/autop': 'wp.autop', - '@wordpress/blob': 'wp.blob', - '@wordpress/block-directory': 'wp.blockDirectory', - '@wordpress/block-editor': 'wp.blockEditor', - '@wordpress/block-library': 'wp.blockLibrary', - '@wordpress/block-serialization-default-parser': - 'wp.blockSerializationDefaultParser', - '@wordpress/blocks': 'wp.blocks', - '@wordpress/components': 'wp.components', - '@wordpress/compose': 'wp.compose', - '@wordpress/core-data': 'wp.coreData', - '@wordpress/data': 'wp.data', - '@wordpress/date': 'wp.date', - '@wordpress/deprecated': 'wp.deprecated', - '@wordpress/dom': 'wp.dom', - '@wordpress/dom-ready': 'wp.domReady', - '@wordpress/edit-navigation': 'wp.editNavigation', - '@wordpress/edit-post': 'wp.editPost', - '@wordpress/edit-site': 'wp.editSite', - '@wordpress/edit-widgets': 'wp.editWidgets', - '@wordpress/editor': 'wp.editor', - '@wordpress/element': 'wp.element', - '@wordpress/escape-html': 'wp.escapeHtml', - '@wordpress/format-library': 'wp.formatLibrary', - '@wordpress/hooks': 'wp.hooks', - '@wordpress/html-entities': 'wp.htmlEntities', - '@wordpress/i18n': 'wp.i18n', - // '@wordpress/icons': 'wp.icons', - '@wordpress/is-shallow-equal': 'wp.isShallowEqual', - '@wordpress/keyboard-shortcuts': 'wp.keyboardShortcuts', - '@wordpress/keycodes': 'wp.keycodes', - '@wordpress/notices': 'wp.notices', - '@wordpress/nux': 'wp.nux', - '@wordpress/plugins': 'wp.plugins', - '@wordpress/preferences': 'wp.preferences', - '@wordpress/preferences-persistence': 'wp.preferencesPersistence', - '@wordpress/primitives': 'wp.primitives', - '@wordpress/reusable-blocks': 'wp.reusableBlocks', - '@wordpress/rich-text': 'wp.richText', - '@wordpress/shortcode': 'wp.shortcode', - '@wordpress/url': 'wp.url', - '@wordpress/viewport': 'wp.viewport', - '@wordpress/warning': 'wp.warning', - '@wordpress/widgets': 'wp.widgets', - '@wordpress/wordcount': 'wp.wordcount', - }[id]; -} diff --git a/tsconfig.base.json b/tsconfig.base.json index a4ecd9ba3d..d319551f61 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -36,9 +36,6 @@ "@wp-playground/client": [ "packages/playground/client/src/index.ts" ], - "@wp-playground/interactive-code-block": [ - "packages/interactive-code-block/src/index.ts" - ], "@wp-playground/node": ["packages/playground/node/src/index.ts"], "@wp-playground/nx-extensions": [ "packages/nx-extensions/src/index.ts"