From 01e538c5c274b9b02dba40ef6d45fe934e5c7f9a Mon Sep 17 00:00:00 2001 From: KoloMl Date: Tue, 12 Nov 2024 15:47:21 +0400 Subject: [PATCH 1/3] Cloning JS formatting settings to the TS, allow importing TS --- .editorconfig | 172 +++++++++++++++++++++++++++++++++++++++++++++++ jsconfig.json | 7 +- svelte.config.js | 6 ++ 3 files changed, 180 insertions(+), 5 deletions(-) diff --git a/.editorconfig b/.editorconfig index b4b0bcb..1c3e0d5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -231,6 +231,178 @@ ij_javascript_while_brace_force = never ij_javascript_while_on_new_line = false ij_javascript_wrap_comments = false +[{*.ts,*.tsx}] +indent_size = 2 +tab_width = 2 +ij_continuation_indent_size = 2 +ij_typescript_align_imports = false +ij_typescript_align_multiline_array_initializer_expression = false +ij_typescript_align_multiline_binary_operation = false +ij_typescript_align_multiline_chained_methods = false +ij_typescript_align_multiline_extends_list = false +ij_typescript_align_multiline_for = true +ij_typescript_align_multiline_parameters = true +ij_typescript_align_multiline_parameters_in_calls = false +ij_typescript_align_multiline_ternary_operation = false +ij_typescript_align_object_properties = 0 +ij_typescript_align_union_types = false +ij_typescript_align_var_statements = 0 +ij_typescript_array_initializer_new_line_after_left_brace = false +ij_typescript_array_initializer_right_brace_on_new_line = false +ij_typescript_array_initializer_wrap = off +ij_typescript_assignment_wrap = off +ij_typescript_binary_operation_sign_on_next_line = false +ij_typescript_binary_operation_wrap = off +ij_typescript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/** +ij_typescript_blank_lines_after_imports = 1 +ij_typescript_blank_lines_around_class = 1 +ij_typescript_blank_lines_around_field = 0 +ij_typescript_blank_lines_around_function = 1 +ij_typescript_blank_lines_around_method = 1 +ij_typescript_block_brace_style = end_of_line +ij_typescript_block_comment_add_space = false +ij_typescript_block_comment_at_first_column = true +ij_typescript_call_parameters_new_line_after_left_paren = false +ij_typescript_call_parameters_right_paren_on_new_line = false +ij_typescript_call_parameters_wrap = off +ij_typescript_catch_on_new_line = false +ij_typescript_chained_call_dot_on_new_line = true +ij_typescript_class_brace_style = end_of_line +ij_typescript_comma_on_new_line = false +ij_typescript_do_while_brace_force = never +ij_typescript_else_on_new_line = false +ij_typescript_enforce_trailing_comma = keep +ij_typescript_extends_keyword_wrap = off +ij_typescript_extends_list_wrap = off +ij_typescript_field_prefix = _ +ij_typescript_file_name_style = relaxed +ij_typescript_finally_on_new_line = false +ij_typescript_for_brace_force = never +ij_typescript_for_statement_new_line_after_left_paren = false +ij_typescript_for_statement_right_paren_on_new_line = false +ij_typescript_for_statement_wrap = off +ij_typescript_force_quote_style = false +ij_typescript_force_semicolon_style = false +ij_typescript_function_expression_brace_style = end_of_line +ij_typescript_if_brace_force = never +ij_typescript_import_merge_members = global +ij_typescript_import_prefer_absolute_path = global +ij_typescript_import_sort_members = true +ij_typescript_import_sort_module_name = false +ij_typescript_import_use_node_resolution = true +ij_typescript_imports_wrap = on_every_item +ij_typescript_indent_case_from_switch = true +ij_typescript_indent_chained_calls = true +ij_typescript_indent_package_children = 0 +ij_typescript_jsx_attribute_value = braces +ij_typescript_keep_blank_lines_in_code = 2 +ij_typescript_keep_first_column_comment = true +ij_typescript_keep_indents_on_empty_lines = false +ij_typescript_keep_line_breaks = true +ij_typescript_keep_simple_blocks_in_one_line = false +ij_typescript_keep_simple_methods_in_one_line = false +ij_typescript_line_comment_add_space = true +ij_typescript_line_comment_at_first_column = false +ij_typescript_method_brace_style = end_of_line +ij_typescript_method_call_chain_wrap = off +ij_typescript_method_parameters_new_line_after_left_paren = false +ij_typescript_method_parameters_right_paren_on_new_line = false +ij_typescript_method_parameters_wrap = off +ij_typescript_object_literal_wrap = on_every_item +ij_typescript_object_types_wrap = on_every_item +ij_typescript_parentheses_expression_new_line_after_left_paren = false +ij_typescript_parentheses_expression_right_paren_on_new_line = false +ij_typescript_place_assignment_sign_on_next_line = false +ij_typescript_prefer_as_type_cast = false +ij_typescript_prefer_explicit_types_function_expression_returns = false +ij_typescript_prefer_explicit_types_function_returns = false +ij_typescript_prefer_explicit_types_vars_fields = false +ij_typescript_prefer_parameters_wrap = false +ij_typescript_property_prefix = +ij_typescript_reformat_c_style_comments = false +ij_typescript_space_after_colon = true +ij_typescript_space_after_comma = true +ij_typescript_space_after_dots_in_rest_parameter = false +ij_typescript_space_after_generator_mult = true +ij_typescript_space_after_property_colon = true +ij_typescript_space_after_quest = true +ij_typescript_space_after_type_colon = true +ij_typescript_space_after_unary_not = false +ij_typescript_space_before_async_arrow_lparen = true +ij_typescript_space_before_catch_keyword = true +ij_typescript_space_before_catch_left_brace = true +ij_typescript_space_before_catch_parentheses = true +ij_typescript_space_before_class_lbrace = true +ij_typescript_space_before_class_left_brace = true +ij_typescript_space_before_colon = true +ij_typescript_space_before_comma = false +ij_typescript_space_before_do_left_brace = true +ij_typescript_space_before_else_keyword = true +ij_typescript_space_before_else_left_brace = true +ij_typescript_space_before_finally_keyword = true +ij_typescript_space_before_finally_left_brace = true +ij_typescript_space_before_for_left_brace = true +ij_typescript_space_before_for_parentheses = true +ij_typescript_space_before_for_semicolon = false +ij_typescript_space_before_function_left_parenth = true +ij_typescript_space_before_generator_mult = false +ij_typescript_space_before_if_left_brace = true +ij_typescript_space_before_if_parentheses = true +ij_typescript_space_before_method_call_parentheses = false +ij_typescript_space_before_method_left_brace = true +ij_typescript_space_before_method_parentheses = false +ij_typescript_space_before_property_colon = false +ij_typescript_space_before_quest = true +ij_typescript_space_before_switch_left_brace = true +ij_typescript_space_before_switch_parentheses = true +ij_typescript_space_before_try_left_brace = true +ij_typescript_space_before_type_colon = false +ij_typescript_space_before_unary_not = false +ij_typescript_space_before_while_keyword = true +ij_typescript_space_before_while_left_brace = true +ij_typescript_space_before_while_parentheses = true +ij_typescript_spaces_around_additive_operators = true +ij_typescript_spaces_around_arrow_function_operator = true +ij_typescript_spaces_around_assignment_operators = true +ij_typescript_spaces_around_bitwise_operators = true +ij_typescript_spaces_around_equality_operators = true +ij_typescript_spaces_around_logical_operators = true +ij_typescript_spaces_around_multiplicative_operators = true +ij_typescript_spaces_around_relational_operators = true +ij_typescript_spaces_around_shift_operators = true +ij_typescript_spaces_around_unary_operator = false +ij_typescript_spaces_within_array_initializer_brackets = false +ij_typescript_spaces_within_brackets = false +ij_typescript_spaces_within_catch_parentheses = false +ij_typescript_spaces_within_for_parentheses = false +ij_typescript_spaces_within_if_parentheses = false +ij_typescript_spaces_within_imports = false +ij_typescript_spaces_within_interpolation_expressions = false +ij_typescript_spaces_within_method_call_parentheses = false +ij_typescript_spaces_within_method_parentheses = false +ij_typescript_spaces_within_object_literal_braces = false +ij_typescript_spaces_within_object_type_braces = true +ij_typescript_spaces_within_parentheses = false +ij_typescript_spaces_within_switch_parentheses = false +ij_typescript_spaces_within_type_assertion = false +ij_typescript_spaces_within_union_types = true +ij_typescript_spaces_within_while_parentheses = false +ij_typescript_special_else_if_treatment = true +ij_typescript_ternary_operation_signs_on_next_line = false +ij_typescript_ternary_operation_wrap = off +ij_typescript_union_types_wrap = on_every_item +ij_typescript_use_chained_calls_group_indents = false +ij_typescript_use_double_quotes = true +ij_typescript_use_explicit_js_extension = auto +ij_typescript_use_import_type = auto +ij_typescript_use_path_mapping = always +ij_typescript_use_public_modifier = false +ij_typescript_use_semicolon_after_statement = true +ij_typescript_var_declaration_wrap = normal +ij_typescript_while_brace_force = never +ij_typescript_while_on_new_line = false +ij_typescript_wrap_comments = false + [{*.htm,*.html,*.sht,*.shtm,*.shtml}] ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3 ij_html_align_attributes = true diff --git a/jsconfig.json b/jsconfig.json index 77187e1..3f97f09 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -9,10 +9,7 @@ "skipLibCheck": true, "sourceMap": true, "strict": true, - "moduleResolution": "bundler" + "moduleResolution": "bundler", + "allowImportingTsExtensions": true } - // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias and https://kit.svelte.dev/docs/configuration#files - // - // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes - // from the referenced tsconfig.json - TypeScript does not merge them in } diff --git a/svelte.config.js b/svelte.config.js index 130d3f1..5532039 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -19,6 +19,12 @@ const config = { "$stores": "./src/stores", "$entities": "./src/lib/extension/entities", }, + typescript: { + config: config => { + config.compilerOptions = config.compilerOptions || {}; + config.compilerOptions.allowImportingTsExtension = true + } + } }, preprocess: [ vitePreprocess({ From c15fae7c3dd31b480831cb9e44563ed6041faa4b Mon Sep 17 00:00:00 2001 From: KoloMl Date: Tue, 12 Nov 2024 16:19:13 +0400 Subject: [PATCH 2/3] Implemented separate class for importing/exporting the entities --- src/lib/extension/EntitiesTransporter.ts | 73 ++++++++++++++++++++ src/lib/extension/transporting/exporters.js | 26 +++++++ src/lib/extension/transporting/validators.js | 39 +++++++++++ 3 files changed, 138 insertions(+) create mode 100644 src/lib/extension/EntitiesTransporter.ts create mode 100644 src/lib/extension/transporting/exporters.js create mode 100644 src/lib/extension/transporting/validators.js diff --git a/src/lib/extension/EntitiesTransporter.ts b/src/lib/extension/EntitiesTransporter.ts new file mode 100644 index 0000000..598edbb --- /dev/null +++ b/src/lib/extension/EntitiesTransporter.ts @@ -0,0 +1,73 @@ +import {validateImportedEntity} from "$lib/extension/transporting/validators.js"; +import {exportEntityToObject} from "$lib/extension/transporting/exporters.js"; +import StorageEntity from "./base/StorageEntity.js"; +import {compressToEncodedURIComponent, decompressFromEncodedURIComponent} from "lz-string"; + +type EntityConstructor = + (new (id: string, settings: Record) => T) + & typeof StorageEntity; + +export default class EntitiesTransporter { + readonly #targetEntityConstructor: EntityConstructor; + + constructor(entityConstructor: EntityConstructor) { + this.#targetEntityConstructor = entityConstructor; + } + + importFromJSON(jsonString: string): EntityType { + const importedObject = this.#tryParsingAsJSON(jsonString); + + if (!importedObject) { + throw new Error('Invalid JSON!'); + } + + validateImportedEntity( + importedObject, + this.#targetEntityConstructor._entityName + ); + + return new this.#targetEntityConstructor( + importedObject.id, + importedObject + ); + } + + importFromCompressedJSON(compressedJsonString: string): EntityType { + return this.importFromJSON( + decompressFromEncodedURIComponent(compressedJsonString) + ) + } + + exportToJSON(entityObject: EntityType): string { + if (!(entityObject instanceof this.#targetEntityConstructor)) { + throw new TypeError('Transporter should be connected to the same entity to export!'); + } + + const exportableObject = exportEntityToObject( + entityObject, + this.#targetEntityConstructor._entityName + ); + + return JSON.stringify(exportableObject, null, 2); + } + + exportToCompressedJSON(entityObject: EntityType): string { + return compressToEncodedURIComponent(this.exportToJSON(entityObject)); + } + + #tryParsingAsJSON(jsonString: string): Record | null { + let jsonObject: Record | null = null; + + try { + jsonObject = JSON.parse(jsonString); + } catch (e) { + + } + + if (typeof jsonObject !== "object") { + throw new TypeError("Should be an object!"); + } + + return jsonObject + } +} diff --git a/src/lib/extension/transporting/exporters.js b/src/lib/extension/transporting/exporters.js new file mode 100644 index 0000000..f74e15f --- /dev/null +++ b/src/lib/extension/transporting/exporters.js @@ -0,0 +1,26 @@ +/** + * @type {Map Record)>} + */ +const entitiesExporters = new Map([ + ['profiles', /** @param {import('../entities/MaintenanceProfile.js').default} entity */entity => { + return { + v: 1, + id: entity.id, + name: entity.settings.name, + tags: entity.settings.tags, + } + }] +]) + +/** + * @param entityInstance + * @param {string} entityName + * @returns {Record} + */ +export function exportEntityToObject(entityInstance, entityName) { + if (!entitiesExporters.has(entityName)) { + throw new Error(`Missing exporter for entity: ${entityName}`); + } + + return entitiesExporters.get(entityName).call(null, entityInstance); +} diff --git a/src/lib/extension/transporting/validators.js b/src/lib/extension/transporting/validators.js new file mode 100644 index 0000000..cb41e3d --- /dev/null +++ b/src/lib/extension/transporting/validators.js @@ -0,0 +1,39 @@ +/** + * Map of validators for each entity. Function should throw the error if validation failed. + * @type {Map void)>} + */ +const entitiesValidators = new Map([ + ['profiles', importedObject => { + if (importedObject.v !== 1) { + throw new Error('Unsupported version!'); + } + + if ( + !importedObject.id + || typeof importedObject.id !== "string" + || !importedObject.name + || typeof importedObject.name !== "string" + || !importedObject.tags + || !Array.isArray(importedObject.tags) + ) { + throw new Error('Invalid profile format detected!'); + } + }] +]) + +/** + * Validate the structure of the entity. + * @param {Object} importedObject Object imported from JSON. + * @param {string} entityName Name of the entity to validate. Should be loaded from the entity class. + * @throws {Error} Error in case validation failed with the reason stored in the message. + */ +export function validateImportedEntity(importedObject, entityName) { + if (!entitiesValidators.has(entityName)) { + console.error(`Trying to validate entity without the validator present! Entity name: ${entityName}`); + return; + } + + entitiesValidators + .get(entityName) + .call(null, importedObject); +} From 3621bb9f0e2869f6bfb2f1b3c23719b33e9d51f6 Mon Sep 17 00:00:00 2001 From: KoloMl Date: Tue, 12 Nov 2024 16:21:06 +0400 Subject: [PATCH 3/3] Removed export/import logic from entities, using transporter in popup --- .../extension/entities/MaintenanceProfile.js | 77 ------------------- .../maintenance/[id]/export/+page.svelte | 13 ++-- .../features/maintenance/import/+page.svelte | 7 +- 3 files changed, 13 insertions(+), 84 deletions(-) diff --git a/src/lib/extension/entities/MaintenanceProfile.js b/src/lib/extension/entities/MaintenanceProfile.js index 0e0e59f..0f9a637 100644 --- a/src/lib/extension/entities/MaintenanceProfile.js +++ b/src/lib/extension/entities/MaintenanceProfile.js @@ -1,6 +1,5 @@ import StorageEntity from "$lib/extension/base/StorageEntity.js"; import EntitiesController from "$lib/extension/EntitiesController.js"; -import {compressToEncodedURIComponent, decompressFromEncodedURIComponent} from "lz-string"; /** * @typedef {Object} MaintenanceProfileSettings @@ -30,26 +29,6 @@ class MaintenanceProfile extends StorageEntity { return super.settings; } - /** - * Export the profile to the formatted JSON. - * - * @type {string} - */ - toJSON() { - return JSON.stringify({ - v: 1, - id: this.id, - name: this.settings.name, - tags: this.settings.tags, - }, null, 2); - } - - toCompressedJSON() { - return compressToEncodedURIComponent( - this.toJSON() - ); - } - static _entityName = "profiles"; /** @@ -79,62 +58,6 @@ class MaintenanceProfile extends StorageEntity { callback ); } - - /** - * Validate and import the profile from the JSON. - * @param {string} exportedString JSON for profile. - * @return {MaintenanceProfile} Maintenance profile imported from the JSON. Note that profile is not automatically - * saved. - * @throws {Error} When version is unsupported or format is invalid. - */ - static importFromJSON(exportedString) { - let importedObject; - - try { - importedObject = JSON.parse(exportedString); - } catch (e) { - // Error will be sent later, since empty string could be parsed as nothing without raising the error. - } - - if (!importedObject) { - throw new Error('Invalid JSON!'); - } - - if (importedObject.v !== 1) { - throw new Error('Unsupported version!'); - } - - if ( - !importedObject.id - || typeof importedObject.id !== "string" - || !importedObject.name - || typeof importedObject.name !== "string" - || !importedObject.tags - || !Array.isArray(importedObject.tags) - ) { - throw new Error('Invalid profile format detected!'); - } - - return new MaintenanceProfile( - importedObject.id, - { - name: importedObject.name, - tags: importedObject.tags, - } - ); - } - - /** - * Validate and import the profile from the compressed JSON string. - * @param {string} compressedString - * @return {MaintenanceProfile} - * @throws {Error} When version is unsupported or format is invalid. - */ - static importFromCompressedJSON(compressedString) { - return this.importFromJSON( - decompressFromEncodedURIComponent(compressedString) - ); - } } export default MaintenanceProfile; diff --git a/src/routes/features/maintenance/[id]/export/+page.svelte b/src/routes/features/maintenance/[id]/export/+page.svelte index e533014..d4e2191 100644 --- a/src/routes/features/maintenance/[id]/export/+page.svelte +++ b/src/routes/features/maintenance/[id]/export/+page.svelte @@ -1,11 +1,13 @@