Repository: obsidian-tasks-group/obsidian-tasks
Pull request #867 (2022-07-07 09:05:05)

chore: internal: Fix imports only used for types
@esm7 Could you code review this please, when you have a chance? Some of these touch your recent innovations, so I am hoping you can confirm that the imports **are** just being used for types.

## Description
An updated version of svelte's provided `tsconfig` turns on a check for imports that are being used only for types not as values. This PR just fixes the spots that were alerted by this rule; it does not contain the updated rule or any dependency changes.

## Motivation and Context
I was checking through updated dev dependencies on my fork and one of the svelte related updates turns on an additional tsconfig rule to `error` for `import` statements that are being used only for typechecking. This seems like a thing worth fixing even without the dependency upgrade, since marking them as `import type` is both more correct and might reduce the size of the built package.  **However**, since I am not an expert on this code or TypeScript, could use some confirmation that these changes are correct to make - i.e. the imports that have been moved to `import type` really are only being used for type checking! Hoping esm7 would be willing to review.

## How has this been tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, tests ran to see how -->
<!--- your change affects other areas of the code, etc. -->
Passes automated tests locally and on GitHub Actions in my fork.
Smoke tested on Obsidian stable on Windows. (After code review, it would be great if someone could smoke test on insider.)


## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] Refactor (non-breaking change which only improves the design or structure of existing code, and making no changes to its external behaviour)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [x] internal

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [x] My code follows the code style of this project and passes `yarn run lint`.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] My change has adequate Unit Test coverage.

By creating a Pull Request I agree to the [Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md).

esm7: The changes in the files that I added look correct, and if you'd change a needed `import` to an `import type` the code would instantly fail in runtime, so I think we're good in this regard.
Either way, I have no CR rights here and @claremacrae will have the final say :smile: 
claremacrae: I'm very happy to merge this.

What puzzles me is that I have in the past had warnings from WebStorm about exactly such imports. Yet it's not now showing those warnings when I look at the code before this PR. 

Perhaps I turned the check off by mistake...
claremacrae: Oh, it's a draft so I can't merge it.

Patch:
diff --git a/src/Cache.ts b/src/Cache.ts
index 219ec5f1..80afaf2f 100644
--- a/src/Cache.ts
+++ b/src/Cache.ts
@@ -1,11 +1,5 @@
-import {
-    EventRef,
-    MetadataCache,
-    SectionCache,
-    TAbstractFile,
-    TFile,
-    Vault,
-} from 'obsidian';
+import { MetadataCache, TAbstractFile, TFile, Vault } from 'obsidian';
+import type { EventRef, SectionCache } from 'obsidian';
 import { Mutex } from 'async-mutex';
 
 import { Task } from './Task';
diff --git a/src/File.ts b/src/File.ts
index 02fc00a0..b7a8ada9 100644
--- a/src/File.ts
+++ b/src/File.ts
@@ -1,4 +1,5 @@
-import { ListItemCache, MetadataCache, TFile, Vault } from 'obsidian';
+import { MetadataCache, TFile, Vault } from 'obsidian';
+import type { ListItemCache } from 'obsidian';
 
 import { getSettings } from './config/Settings';
 import type { Task } from './Task';
diff --git a/src/LivePreviewExtension.ts b/src/LivePreviewExtension.ts
index 6a4c9003..7a0715fb 100644
--- a/src/LivePreviewExtension.ts
+++ b/src/LivePreviewExtension.ts
@@ -1,4 +1,5 @@
-import { EditorView, PluginValue, ViewPlugin } from '@codemirror/view';
+import { EditorView, ViewPlugin } from '@codemirror/view';
+import type { PluginValue } from '@codemirror/view';
 
 import { Task } from './Task';
 
diff --git a/src/Query/Filter/BooleanField.ts b/src/Query/Filter/BooleanField.ts
index f1ba6431..98626bd8 100644
--- a/src/Query/Filter/BooleanField.ts
+++ b/src/Query/Filter/BooleanField.ts
@@ -1,9 +1,11 @@
-import { PostfixExpression, parse as boonParse } from 'boon-js';
+import { parse as boonParse } from 'boon-js';
+import type { PostfixExpression } from 'boon-js';
 
 import { parseFilter } from '../FilterParser';
 import type { Task } from '../../Task';
 import { Field } from './Field';
-import { Filter, FilterOrErrorMessage } from './Filter';
+import { FilterOrErrorMessage } from './Filter';
+import type { Filter } from './Filter';
 
 /**
  * BooleanField is a 'container' field type that parses a high-level filtering query of
@@ -59,17 +61,18 @@ export class BooleanField extends Field {
             // final token in the expression
             for (const token of postfixExpression) {
                 if (token.name === 'IDENTIFIER' && token.value) {
-                    if (!(token.value in this.subFields)) {
-                        const parsedField = parseFilter(token.value);
+                    const identifier = token.value.trim();
+                    if (!(identifier in this.subFields)) {
+                        const parsedField = parseFilter(identifier);
                         if (parsedField === null) {
-                            result.error = `couldn't parse sub-expression '${token.value}'`;
+                            result.error = `couldn't parse sub-expression '${identifier}'`;
                             return result;
                         }
                         if (parsedField.error) {
-                            result.error = `couldn't parse sub-expression '${token.value}': ${parsedField.error}`;
+                            result.error = `couldn't parse sub-expression '${identifier}': ${parsedField.error}`;
                             return result;
                         } else if (parsedField.filter) {
-                            this.subFields[token.value] = parsedField.filter;
+                            this.subFields[identifier] = parsedField.filter;
                         }
                     }
                 } else if (token.name === 'OPERATOR') {
@@ -132,7 +135,7 @@ export class BooleanField extends Field {
                 // For each identifier we created earlier the corresponding Filter, so now we can just evaluate the given
                 // task for each identifier that we find in the postfix expression.
                 if (token.value == null) throw Error('null token value'); // This should not happen
-                const filter = this.subFields[token.value];
+                const filter = this.subFields[token.value.trim()];
                 const result = filter(task);
                 booleanStack.push(toString(result));
             } else if (token.name === 'OPERATOR') {
diff --git a/src/Query/Filter/FilterInstruction.ts b/src/Query/Filter/FilterInstruction.ts
index 55a3dc01..040acb17 100644
--- a/src/Query/Filter/FilterInstruction.ts
+++ b/src/Query/Filter/FilterInstruction.ts
@@ -1,4 +1,5 @@
-import { Filter, FilterOrErrorMessage } from './Filter';
+import { FilterOrErrorMessage } from './Filter';
+import type { Filter } from './Filter';
 
 /**
  * Implementation of a single instruction for filtering tasks, and its corresponding predicate.
diff --git a/src/Query/Filter/FilterInstructions.ts b/src/Query/Filter/FilterInstructions.ts
index a3397e84..d1140cd5 100644
--- a/src/Query/Filter/FilterInstructions.ts
+++ b/src/Query/Filter/FilterInstructions.ts
@@ -1,5 +1,6 @@
 import { FilterInstruction } from './FilterInstruction';
-import { Filter, FilterOrErrorMessage } from './Filter';
+import { FilterOrErrorMessage } from './Filter';
+import type { Filter } from './Filter';
 
 /**
  * Implementation of a collection of instructions for filtering tasks.
diff --git a/src/QueryRenderer.ts b/src/QueryRenderer.ts
index 08fdee20..aded48ad 100644
--- a/src/QueryRenderer.ts
+++ b/src/QueryRenderer.ts
@@ -1,11 +1,5 @@
-import {
-    App,
-    EventRef,
-    MarkdownPostProcessorContext,
-    MarkdownRenderChild,
-    Plugin,
-    TFile,
-} from 'obsidian';
+import { App, MarkdownRenderChild, Plugin, TFile } from 'obsidian';
+import type { EventRef, MarkdownPostProcessorContext } from 'obsidian';
 
 import type { IQuery } from './IQuery';
 import { State } from './Cache';
diff --git a/src/Suggestor/EditorSuggestorPopup.ts b/src/Suggestor/EditorSuggestorPopup.ts
index 106e017c..19d4a697 100644
--- a/src/Suggestor/EditorSuggestorPopup.ts
+++ b/src/Suggestor/EditorSuggestorPopup.ts
@@ -1,16 +1,14 @@
-import {
-    App,
-    Editor,
+import { App, Editor, EditorSuggest, TFile } from 'obsidian';
+import type {
     EditorPosition,
-    EditorSuggest,
     EditorSuggestContext,
     EditorSuggestTriggerInfo,
-    TFile,
 } from 'obsidian';
 
 import type { Settings } from '../config/Settings';
 import * as task from '../Task';
-import { SuggestInfo, buildSuggestions } from './Suggestor';
+import { buildSuggestions } from './Suggestor';
+import type { SuggestInfo } from './Suggestor';
 
 export type SuggestInfoWithContext = SuggestInfo & {
     context: EditorSuggestContext;


Test patch:
diff --git a/tests/EditorSuggest.test.ts b/tests/EditorSuggest.test.ts
index c9177c78..6213f2a5 100644
--- a/tests/EditorSuggest.test.ts
+++ b/tests/EditorSuggest.test.ts
@@ -3,7 +3,8 @@
  */
 import moment from 'moment';
 import { getSettings } from '../src/config/Settings';
-import { SuggestInfo, buildSuggestions } from '../src/Suggestor/Suggestor';
+import { buildSuggestions } from '../src/Suggestor/Suggestor';
+import type { SuggestInfo } from '../src/Suggestor/Suggestor';
 
 window.moment = moment;
 
diff --git a/tests/Query.test.ts b/tests/Query.test.ts
index acdd8521..9dd02285 100644
--- a/tests/Query.test.ts
+++ b/tests/Query.test.ts
@@ -5,10 +5,8 @@ import moment from 'moment';
 import { Query } from '../src/Query';
 import { Priority, Status, Task } from '../src/Task';
 import { createTasksFromMarkdown, fromLine } from './TestHelpers';
-import {
-    FilteringCase,
-    shouldSupportFiltering,
-} from './TestingTools/FilterTestHelpers';
+import { shouldSupportFiltering } from './TestingTools/FilterTestHelpers';
+import type { FilteringCase } from './TestingTools/FilterTestHelpers';
 
 window.moment = moment;
 
diff --git a/tests/Query/Filter/BooleanField.test.ts b/tests/Query/Filter/BooleanField.test.ts
index f68ff068..7205e448 100644
--- a/tests/Query/Filter/BooleanField.test.ts
+++ b/tests/Query/Filter/BooleanField.test.ts
@@ -96,6 +96,18 @@ describe('boolean query', () => {
             testWithDescription(filter, 'd2', false);
             testWithDescription(filter, 'd1 d2', true);
         });
+
+        it('input components dirty with whitespaces', () => {
+            const filter = new BooleanField().createFilterOrErrorMessage(
+                ' (description includes #context/location1) OR (description includes #context/location2 ) OR (  description includes #context/location3 ) OR   (  description includes #context/location4 )',
+            );
+
+            testWithDescription(filter, 'none', false);
+            testWithDescription(filter, 'xxx #context/location1', true);
+            testWithDescription(filter, 'xxx #context/location2', true);
+            testWithDescription(filter, 'xxx #context/location3', true);
+            testWithDescription(filter, 'xxx #context/location4', true);
+        });
     });
 
     describe('error cases - to show error messages', () => {