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="<div> "html""$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 (
-
- );
-}
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 (
-
- );
-}
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:
-
- Run
-
-
- {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 (
-
- );
-}
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 (
- (
-
- )}
- renderContent={({ onClose }) => (
-
-
- {options.map((option) => {
- const isSelected =
- option.value === selectedOption?.value;
- return (
- {
- onChange(option.value);
- onClose();
- }}
- children={option.label}
- />
- );
- })}
-
-
- )}
- {...props}
- />
- );
-};
-
-export default ToolbarDropdown;
diff --git a/packages/interactive-code-block/src/lib/hooks/use-has-transition-class-name.tsx b/packages/interactive-code-block/src/lib/hooks/use-has-transition-class-name.tsx
deleted file mode 100644
index 8da2baed21..0000000000
--- a/packages/interactive-code-block/src/lib/hooks/use-has-transition-class-name.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { useEffect, useState } from 'react';
-import { useRef } from 'react';
-
-export function useHasTransitionClassName(
- isActive: boolean,
- minActiveDuration: number
-) {
- const [hasClass, setHasClass] = useState(isActive);
- const lastUpdate = useRef(Date.now());
- useEffect(() => {
- if (isActive) {
- lastUpdate.current = Date.now();
- setHasClass(true);
- }
-
- const remainingTime = Math.max(
- 0,
- lastUpdate.current + minActiveDuration - Date.now()
- );
- const timeout = setTimeout(() => {
- lastUpdate.current = Date.now();
- setHasClass(false);
- }, remainingTime);
- return () => clearTimeout(timeout);
- }, [isActive]);
-
- return hasClass;
-}
diff --git a/packages/interactive-code-block/src/lib/hooks/use-libraries.tsx b/packages/interactive-code-block/src/lib/hooks/use-libraries.tsx
deleted file mode 100644
index 3678b90b3c..0000000000
--- a/packages/interactive-code-block/src/lib/hooks/use-libraries.tsx
+++ /dev/null
@@ -1,143 +0,0 @@
-import * as React from 'react';
-import { useState, useEffect } from '@wordpress/element';
-import { cloneResponseMonitorProgress } from '@php-wasm/progress';
-import type { Library } from '../types';
-
-type LibraryCache = Record<
- Library['id'],
- {
- progress: number;
- name: string;
- contents?: Uint8Array;
- }
->;
-
-class LibraryLoader extends EventTarget {
- private cache: LibraryCache = {};
-
- private async loadLibrary(library: Library) {
- // Loading is already in progress
- if (this.cache[library.id]) {
- return;
- }
- this.cache[library.id] = {
- progress: 0,
- name: library.name,
- };
- const reportingResponse = cloneResponseMonitorProgress(
- await fetch(library.url),
- (e) => {
- const { loaded, total } = e.detail;
- this.setLibraryProgress(library.id, loaded / total);
- }
- );
- const blob = await reportingResponse.blob();
- this.cache[library.id].contents = new Uint8Array(
- await blob.arrayBuffer()
- );
- this.dispatchEvent(
- new CustomEvent('load', {
- detail: {
- libraryId: library.id,
- },
- })
- );
- }
-
- private setLibraryProgress(libraryId: string, progress: number) {
- this.cache[libraryId].progress = progress;
- this.dispatchEvent(
- new CustomEvent('progress', {
- detail: {
- libraryId,
- progress,
- },
- })
- );
- }
-
- getLoadingProgress(libraryIds: string | string[]) {
- if (!Array.isArray(libraryIds)) {
- libraryIds = [libraryIds];
- }
-
- if (libraryIds.length === 0) {
- return undefined;
- }
-
- return (
- (100 *
- libraryIds.reduce(
- (total, id) => total + this.cache[id]?.progress || 0,
- 0
- )) /
- libraryIds.length
- );
- }
-
- isLoaded(libraryIds: string | string[]) {
- if (!Array.isArray(libraryIds)) {
- libraryIds = [libraryIds];
- }
-
- return libraryIds.every((id) => this.cache[id]?.contents);
- }
-
- getLibraryContents(libraryId: string): Uint8Array | undefined {
- return this.cache[libraryId]?.contents;
- }
-
- getLibrariesContents(libraryIds: string[]): Record {
- return Object.fromEntries(
- libraryIds
- .map((id) => [this.cache[id].name, this.getLibraryContents(id)])
- .filter(([, contents]) => contents)
- );
- }
-
- async load(libraries: Library[]) {
- libraries.forEach((library) => this.loadLibrary(library));
- }
-}
-
-const loader = new LibraryLoader();
-export interface UseLibrariesResult {
- status: 'ready' | 'loading';
- progress?: number;
- loaded: Record;
-}
-
-export function useLibraries(libraries: Library[]): UseLibrariesResult {
- const ids = libraries.map(({ id }) => id);
- const isLoaded = loader.isLoaded(ids);
- const [loadedLibraries, setLoadedLibraries] = useState<
- Record
- >(() => (isLoaded ? loader.getLibrariesContents(ids) : {}));
-
- // Load libraries
- useEffect(() => {
- if (loader.isLoaded(ids)) {
- setLoadedLibraries(loader.getLibrariesContents(ids));
- return;
- }
-
- loader.load(libraries);
-
- function onLoad() {
- if (loader.isLoaded(ids)) {
- setLoadedLibraries(loader.getLibrariesContents(ids));
- }
- }
-
- loader.addEventListener('load', onLoad);
- return () => {
- loader.removeEventListener('load', onLoad);
- };
- }, [libraries]);
-
- return {
- status: isLoaded ? 'ready' : 'loading',
- progress: loader.getLoadingProgress(ids),
- loaded: loadedLibraries,
- };
-}
diff --git a/packages/interactive-code-block/src/lib/hooks/use-on-escape-key.tsx b/packages/interactive-code-block/src/lib/hooks/use-on-escape-key.tsx
deleted file mode 100644
index bd0e376c12..0000000000
--- a/packages/interactive-code-block/src/lib/hooks/use-on-escape-key.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { useEffect } from '@wordpress/element';
-
-export const useOnEscapeKey = (callback) => {
- const handleKeyDown = (event) => {
- if (event.key === 'Escape') {
- callback();
- }
- };
-
- useEffect(() => {
- document.addEventListener('keydown', handleKeyDown);
- return () => {
- document.removeEventListener('keydown', handleKeyDown);
- };
- }, [callback]);
-};
diff --git a/packages/interactive-code-block/src/lib/hooks/use-promise.ts b/packages/interactive-code-block/src/lib/hooks/use-promise.ts
deleted file mode 100644
index 746bf23067..0000000000
--- a/packages/interactive-code-block/src/lib/hooks/use-promise.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { useEffect, useState } from '@wordpress/element';
-
-export function usePromise(promise: Promise) {
- const [isResolved, setIsResolved] = useState(false);
- useEffect(() => {
- promise.then(() => setIsResolved(true));
- }, [promise]);
- return isResolved;
-}
diff --git a/packages/interactive-code-block/src/lib/pages/entities.tsx b/packages/interactive-code-block/src/lib/pages/entities.tsx
deleted file mode 100644
index a112bd1002..0000000000
--- a/packages/interactive-code-block/src/lib/pages/entities.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { dispatch } from '@wordpress/data';
-import { store as coreStore } from '@wordpress/core-data';
-import { ExecutionScript, Library } from '../types';
-
-export function installEntities() {
- (dispatch(coreStore) as any).addEntities([
- {
- kind: 'interactive-code-block',
- name: 'library',
- baseURL: `/interactive-code-block/v1/libraries`,
- baseURLParams: { context: 'edit' },
- label: 'PHP Library',
- plural: 'PHP Libraries',
- getTitle: (record: Library) => record.name,
- },
- {
- kind: 'interactive-code-block',
- name: 'script',
- baseURL: `/interactive-code-block/v1/execution-scripts`,
- baseURLParams: { context: 'edit' },
- label: 'Execution script',
- plural: 'Execution scripts',
- getTitle: (record: ExecutionScript) => record.name,
- },
- ]);
-}
diff --git a/packages/interactive-code-block/src/lib/pages/execution-scripts-forms.tsx b/packages/interactive-code-block/src/lib/pages/execution-scripts-forms.tsx
deleted file mode 100644
index a6af44cdf9..0000000000
--- a/packages/interactive-code-block/src/lib/pages/execution-scripts-forms.tsx
+++ /dev/null
@@ -1,329 +0,0 @@
-import * as React from '@wordpress/element';
-import {
- Button,
- BaseControl,
- TextControl,
- SelectControl,
- Modal,
- Flex,
- FlexItem,
-} from '@wordpress/components';
-import { useState, useRef, useMemo } from '@wordpress/element';
-import { useSelect, useDispatch } from '@wordpress/data';
-import { store as coreDataStore } from '@wordpress/core-data';
-import { store as noticesStore } from '@wordpress/notices';
-import { ToggleControl } from '@wordpress/components';
-
-import CodeMirror from '../components/code-mirror';
-import LibrariesControl from '../components/libraries-control';
-import { ExecutionScript, outputFormats } from '../types';
-import {
- isDefaultScriptId,
- getDefaultExecutionScript,
- SUPPORTED_RUNNERS,
-} from '../components/code-runner';
-import PHPRunner from '../components/php-runner';
-
-export function CreateScriptButton() {
- const [isOpen, setOpen] = useState(false);
- const openModal = () => setOpen(true);
- const closeModal = () => setOpen(false);
- return (
- <>
-
- + Add Script
-
- {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 (
- <>
-
- {isDefaultScriptId(id) ? 'View' : 'Edit'}
-
- {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 (
-
- );
-}
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) && (
-
- {__('Delete')}
-
- )}
-
-
- );
-};
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.
-
-
-
-
-
-
- Upload a library
-
-
-
-
- );
-}
-
-// 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}
-
-
-
- {__('Delete')}
-
-
-
- );
-};
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"