diff --git a/core/modules/field/migration_templates/d6_field.yml b/core/modules/field/migration_templates/d6_field.yml
index f688842..6580780 100644
--- a/core/modules/field/migration_templates/d6_field.yml
+++ b/core/modules/field/migration_templates/d6_field.yml
@@ -2,8 +2,8 @@ id: d6_field
 label: Field configuration
 migration_tags:
   - Drupal 6
-class: Drupal\migrate_drupal\Plugin\migrate\CckMigration
-cck_plugin_method: processField
+class: Drupal\migrate_drupal\Plugin\migrate\FieldMigration
+field_plugin_method: processField
 source:
   plugin: d6_field
   constants:
diff --git a/core/modules/field/migration_templates/d6_field_formatter_settings.yml b/core/modules/field/migration_templates/d6_field_formatter_settings.yml
index d604491..3535e2a 100644
--- a/core/modules/field/migration_templates/d6_field_formatter_settings.yml
+++ b/core/modules/field/migration_templates/d6_field_formatter_settings.yml
@@ -2,8 +2,8 @@ id: d6_field_formatter_settings
 label: Field formatter configuration
 migration_tags:
   - Drupal 6
-class: Drupal\migrate_drupal\Plugin\migrate\CckMigration
-cck_plugin_method: processFieldFormatter
+class: Drupal\migrate_drupal\Plugin\migrate\FieldMigration
+field_plugin_method: processFieldFormatter
 source:
   plugin: d6_field_instance_per_view_mode
   constants:
diff --git a/core/modules/field/migration_templates/d6_field_instance.yml b/core/modules/field/migration_templates/d6_field_instance.yml
index 1934303..3a413c1 100644
--- a/core/modules/field/migration_templates/d6_field_instance.yml
+++ b/core/modules/field/migration_templates/d6_field_instance.yml
@@ -2,8 +2,8 @@ id: d6_field_instance
 label: Field instance configuration
 migration_tags:
   - Drupal 6
-class: Drupal\migrate_drupal\Plugin\migrate\CckMigration
-cck_plugin_method: processFieldInstance
+class: Drupal\migrate_drupal\Plugin\migrate\FieldMigration
+field_plugin_method: processFieldInstance
 source:
   plugin: d6_field_instance
   constants:
diff --git a/core/modules/field/migration_templates/d6_field_instance_widget_settings.yml b/core/modules/field/migration_templates/d6_field_instance_widget_settings.yml
index 90009b7..be54b54 100644
--- a/core/modules/field/migration_templates/d6_field_instance_widget_settings.yml
+++ b/core/modules/field/migration_templates/d6_field_instance_widget_settings.yml
@@ -2,8 +2,8 @@ id: d6_field_instance_widget_settings
 label: Field instance widget configuration
 migration_tags:
   - Drupal 6
-class: Drupal\migrate_drupal\Plugin\migrate\CckMigration
-cck_plugin_method: processFieldWidget
+class: Drupal\migrate_drupal\Plugin\migrate\FieldMigration
+field_plugin_method: processFieldWidget
 source:
   plugin: d6_field_instance_per_form_display
   constants:
diff --git a/core/modules/field/migration_templates/d7_field.yml b/core/modules/field/migration_templates/d7_field.yml
index 15079d8..d455f8a 100644
--- a/core/modules/field/migration_templates/d7_field.yml
+++ b/core/modules/field/migration_templates/d7_field.yml
@@ -2,8 +2,8 @@ id: d7_field
 label: Field configuration
 migration_tags:
   - Drupal 7
-class: Drupal\migrate_drupal\Plugin\migrate\CckMigration
-cck_plugin_method: processField
+class: Drupal\migrate_drupal\Plugin\migrate\FieldMigration
+field_plugin_method: processField
 source:
   plugin: d7_field
   constants:
diff --git a/core/modules/field/migration_templates/d7_field_formatter_settings.yml b/core/modules/field/migration_templates/d7_field_formatter_settings.yml
index 126fd29..295c04f 100644
--- a/core/modules/field/migration_templates/d7_field_formatter_settings.yml
+++ b/core/modules/field/migration_templates/d7_field_formatter_settings.yml
@@ -2,8 +2,8 @@ id: d7_field_formatter_settings
 label: Field formatter configuration
 migration_tags:
   - Drupal 7
-class: Drupal\migrate_drupal\Plugin\migrate\CckMigration
-cck_plugin_method: processFieldFormatter
+class: Drupal\migrate_drupal\Plugin\migrate\FieldMigration
+field_plugin_method: processFieldFormatter
 source:
   plugin: d7_field_instance_per_view_mode
   constants:
diff --git a/core/modules/field/migration_templates/d7_field_instance.yml b/core/modules/field/migration_templates/d7_field_instance.yml
index f3518c9..1762793 100644
--- a/core/modules/field/migration_templates/d7_field_instance.yml
+++ b/core/modules/field/migration_templates/d7_field_instance.yml
@@ -2,8 +2,8 @@ id: d7_field_instance
 label: Field instance configuration
 migration_tags:
   - Drupal 7
-class: Drupal\migrate_drupal\Plugin\migrate\CckMigration
-cck_plugin_method: processFieldInstance
+class: Drupal\migrate_drupal\Plugin\migrate\FieldMigration
+field_plugin_method: processFieldInstance
 source:
   plugin: d7_field_instance
   constants:
diff --git a/core/modules/field/migration_templates/d7_field_instance_widget_settings.yml b/core/modules/field/migration_templates/d7_field_instance_widget_settings.yml
index e2bbcf4..ff90dce 100644
--- a/core/modules/field/migration_templates/d7_field_instance_widget_settings.yml
+++ b/core/modules/field/migration_templates/d7_field_instance_widget_settings.yml
@@ -2,8 +2,8 @@ id: d7_field_instance_widget_settings
 label: Field instance widget configuration
 migration_tags:
   - Drupal 7
-class: Drupal\migrate_drupal\Plugin\migrate\CckMigration
-cck_plugin_method: processFieldWidget
+class: Drupal\migrate_drupal\Plugin\migrate\FieldMigration
+field_plugin_method: processFieldWidget
 source:
   plugin: d7_field_instance_per_form_display
   constants:
diff --git a/core/modules/file/src/Plugin/migrate/field/d6/FileField.php b/core/modules/file/src/Plugin/migrate/field/d6/FileField.php
new file mode 100644
index 0000000..930c13b
--- /dev/null
+++ b/core/modules/file/src/Plugin/migrate/field/d6/FileField.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Drupal\file\Plugin\migrate\field\d6;
+
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate\Row;
+use Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase;
+
+/**
+ * @MigrateField(
+ *   id = "filefield",
+ *   core = {6}
+ * )
+ */
+class FileField extends FieldPluginBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldWidgetMap() {
+    return [
+      'filefield_widget' => 'file_generic',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldFormatterMap() {
+    return [
+      'default' => 'file_default',
+      'url_plain' => 'file_url_plain',
+      'path_plain' => 'file_url_plain',
+      'image_plain' => 'image',
+      'image_nodelink' => 'image',
+      'image_imagelink' => 'image',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processFieldValues(MigrationInterface $migration, $field_name, $data) {
+    $process = [
+      'plugin' => 'd6_field_file',
+      'source' => $field_name,
+    ];
+    $migration->mergeProcessOfProperty($field_name, $process);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldType(Row $row) {
+    return $row->getSourceProperty('widget_type') == 'imagefield_widget' ? 'image' : 'file';
+  }
+
+}
diff --git a/core/modules/file/src/Plugin/migrate/field/d7/FileField.php b/core/modules/file/src/Plugin/migrate/field/d7/FileField.php
new file mode 100644
index 0000000..8430c8b
--- /dev/null
+++ b/core/modules/file/src/Plugin/migrate/field/d7/FileField.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace Drupal\file\Plugin\migrate\field\d7;
+
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate\Row;
+use Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase;
+
+/**
+ * @MigrateField(
+ *   id = "file",
+ *   core = {7}
+ * )
+ */
+class FileField extends FieldPluginBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldWidgetMap() {
+    return [
+      'filefield_widget' => 'file_generic',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldFormatterMap() {
+    return [
+      'default' => 'file_default',
+      'url_plain' => 'file_url_plain',
+      'path_plain' => 'file_url_plain',
+      'image_plain' => 'image',
+      'image_nodelink' => 'image',
+      'image_imagelink' => 'image',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processFieldValues(MigrationInterface $migration, $field_name, $data) {
+    $process = [
+      'plugin' => 'iterator',
+      'source' => $field_name,
+      'process' => [
+        'target_id' => 'fid',
+        'display' => 'display',
+        'description' => 'description',
+      ],
+    ];
+    $migration->mergeProcessOfProperty($field_name, $process);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldType(Row $row) {
+    return $row->getSourceProperty('widget_type') == 'imagefield_widget' ? 'image' : 'file';
+  }
+
+}
diff --git a/core/modules/file/src/Plugin/migrate/field/d7/ImageField.php b/core/modules/file/src/Plugin/migrate/field/d7/ImageField.php
new file mode 100644
index 0000000..3688972
--- /dev/null
+++ b/core/modules/file/src/Plugin/migrate/field/d7/ImageField.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Drupal\file\Plugin\migrate\field\d7;
+
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase;
+
+/**
+ * @MigrateField(
+ *   id = "image",
+ *   core = {7}
+ * )
+ */
+class ImageField extends FieldPluginBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldFormatterMap() {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processFieldValues(MigrationInterface $migration, $field_name, $data) {
+    $process = [
+      'plugin' => 'iterator',
+      'source' => $field_name,
+      'process' => [
+        'target_id' => 'fid',
+        'alt' => 'alt',
+        'title' => 'title',
+        'width' => 'width',
+        'height' => 'height',
+      ],
+    ];
+    $migration->mergeProcessOfProperty($field_name, $process);
+  }
+
+}
diff --git a/core/modules/file/src/Plugin/migrate/process/d6/CckFile.php b/core/modules/file/src/Plugin/migrate/process/d6/CckFile.php
index 8cf2d19..19c0dbf 100644
--- a/core/modules/file/src/Plugin/migrate/process/d6/CckFile.php
+++ b/core/modules/file/src/Plugin/migrate/process/d6/CckFile.php
@@ -2,91 +2,17 @@
 
 namespace Drupal\file\Plugin\migrate\process\d6;
 
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\migrate\Plugin\MigrationInterface;
-use Drupal\migrate\MigrateExecutableInterface;
-use Drupal\migrate\Plugin\MigrateProcessInterface;
-use Drupal\migrate\ProcessPluginBase;
-use Drupal\migrate\Row;
-use Symfony\Component\DependencyInjection\ContainerInterface;
+@trigger_error('CckFile is deprecated in Drupal 8.3.x and will be
+be removed before Drupal 9.0.x. Use \Drupal\file\Plugin\migrate\process\d6\FieldFile
+instead.', E_USER_DEPRECATED);
+
 
 /**
  * @MigrateProcessPlugin(
  *   id = "d6_cck_file"
  * )
+ *
+ *  @deprecated in Drupal 8.3.x, to be removed before Drupal 9.0.x. Use
+ * \Drupal\file\Plugin\migrate\process\d6\FieldFile instead.
  */
-class CckFile extends ProcessPluginBase implements ContainerFactoryPluginInterface {
-
-  /**
-   * The migration process plugin, configured for lookups in d6_file.
-   *
-   * @var \Drupal\migrate\Plugin\MigrateProcessInterface
-   */
-  protected $migrationPlugin;
-
-  /**
-   * Constructs a CckFile plugin instance.
-   *
-   * @param array $configuration
-   *   The plugin configuration.
-   * @param string $plugin_id
-   *   The plugin ID.
-   * @param mixed $plugin_definition
-   *   The plugin definition.
-   * @param \Drupal\migrate\Plugin\MigrationInterface $migration
-   *   The current migration.
-   * @param \Drupal\migrate\Plugin\MigrateProcessInterface $migration_plugin
-   *   An instance of the 'migration' process plugin.
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, MigrateProcessInterface $migration_plugin) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-    $this->migration = $migration;
-    $this->migrationPlugin = $migration_plugin;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
-    // Configure the migration process plugin to look up migrated IDs from
-    // a d6 file migration.
-    $migration_plugin_configuration = $configuration + [
-      'migration' => 'd6_file',
-      'source' => ['fid'],
-    ];
-
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $migration,
-      $container->get('plugin.manager.migrate.process')->createInstance('migration', $migration_plugin_configuration, $migration)
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
-    $options = unserialize($value['data']);
-
-    // Try to look up the ID of the migrated file. If one cannot be found, it
-    // means the file referenced by the current field item did not migrate for
-    // some reason -- file migration is notoriously brittle -- and we do NOT
-    // want to send invalid file references into the field system (it causes
-    // fatals), so return an empty item instead.
-    if ($fid = $this->migrationPlugin->transform($value['fid'], $migrate_executable, $row, $destination_property)) {
-      return [
-        'target_id' => $fid,
-        'display' => $value['list'],
-        'description' => isset($options['description']) ? $options['description'] : '',
-        'alt' => isset($options['alt']) ? $options['alt'] : '',
-        'title' => isset($options['title']) ? $options['title'] : '',
-      ];
-    }
-    else {
-      return [];
-    }
-  }
-
-}
+class CckFile extends FieldFile {}
diff --git a/core/modules/file/src/Plugin/migrate/process/d6/FieldFile.php b/core/modules/file/src/Plugin/migrate/process/d6/FieldFile.php
new file mode 100644
index 0000000..3b907a3
--- /dev/null
+++ b/core/modules/file/src/Plugin/migrate/process/d6/FieldFile.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace Drupal\file\Plugin\migrate\process\d6;
+
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate\MigrateExecutableInterface;
+use Drupal\migrate\Plugin\MigrateProcessInterface;
+use Drupal\migrate\ProcessPluginBase;
+use Drupal\migrate\Row;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @MigrateProcessPlugin(
+ *   id = "d6_field_file"
+ * )
+ */
+class FieldFile extends ProcessPluginBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The migration process plugin, configured for lookups in d6_file.
+   *
+   * @var \Drupal\migrate\Plugin\MigrateProcessInterface
+   */
+  protected $migrationPlugin;
+
+  /**
+   * Constructs a FieldFile plugin instance.
+   *
+   * @param array $configuration
+   *   The plugin configuration.
+   * @param string $plugin_id
+   *   The plugin ID.
+   * @param mixed $plugin_definition
+   *   The plugin definition.
+   * @param \Drupal\migrate\Plugin\MigrationInterface $migration
+   *   The current migration.
+   * @param \Drupal\migrate\Plugin\MigrateProcessInterface $migration_plugin
+   *   An instance of the 'migration' process plugin.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, MigrateProcessInterface $migration_plugin) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->migration = $migration;
+    $this->migrationPlugin = $migration_plugin;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
+    // Configure the migration process plugin to look up migrated IDs from
+    // a d6 file migration.
+    $migration_plugin_configuration = $configuration + [
+      'migration' => 'd6_file',
+      'source' => ['fid'],
+    ];
+
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $migration,
+      $container->get('plugin.manager.migrate.process')->createInstance('migration', $migration_plugin_configuration, $migration)
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
+    $options = unserialize($value['data']);
+
+    // Try to look up the ID of the migrated file. If one cannot be found, it
+    // means the file referenced by the current field item did not migrate for
+    // some reason -- file migration is notoriously brittle -- and we do NOT
+    // want to send invalid file references into the field system (it causes
+    // fatals), so return an empty item instead.
+    if ($fid = $this->migrationPlugin->transform($value['fid'], $migrate_executable, $row, $destination_property)) {
+      return [
+        'target_id' => $fid,
+        'display' => $value['list'],
+        'description' => isset($options['description']) ? $options['description'] : '',
+        'alt' => isset($options['alt']) ? $options['alt'] : '',
+        'title' => isset($options['title']) ? $options['title'] : '',
+      ];
+    }
+    else {
+      return [];
+    }
+  }
+
+}
diff --git a/core/modules/file/tests/src/Unit/Plugin/migrate/cckfield/d6/FileCckTest.php b/core/modules/file/tests/src/Unit/Plugin/migrate/cckfield/d6/FileCckTest.php
new file mode 100644
index 0000000..ca99b56
--- /dev/null
+++ b/core/modules/file/tests/src/Unit/Plugin/migrate/cckfield/d6/FileCckTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Drupal\Tests\file\Unit\Plugin\migrate\cckfield\d6;
+
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate\Row;
+use Drupal\Tests\UnitTestCase;
+use Drupal\file\Plugin\migrate\cckfield\d6\FileField;
+use Prophecy\Argument;
+
+/**
+ * @coversDefaultClass \Drupal\file\Plugin\migrate\cckfield\d6\FileField
+ * @group file
+ */
+class FileCckTest extends UnitTestCase {
+
+  /**
+   * @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface
+   */
+  protected $plugin;
+
+  /**
+   * @var \Drupal\migrate\Plugin\MigrationInterface
+   */
+  protected $migration;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->plugin = new FileField([], 'file', []);
+
+    $migration = $this->prophesize(MigrationInterface::class);
+
+    // The plugin's processFieldValues() method will call
+    // mergeProcessOfProperty() and return nothing. So, in order to examine the
+    // process pipeline created by the plugin, we need to ensure that
+    // getProcess() always returns the last input to mergeProcessOfProperty().
+    $migration->mergeProcessOfProperty(Argument::type('string'), Argument::type('array'))
+      ->will(function($arguments) use ($migration) {
+        $migration->getProcess()->willReturn($arguments[1]);
+      });
+    $this->migration = $migration->reveal();
+  }
+
+  /**
+   * @covers ::processCckFieldValues
+   */
+  public function testProcessCckFieldValues() {
+    $this->plugin->processCckFieldValues($this->migration, 'somefieldname', []);
+
+    $expected = [
+      'plugin' => 'd6_cck_file',
+      'source' => 'somefieldname',
+    ];
+    $this->assertSame($expected, $this->migration->getProcess());
+  }
+
+  /**
+   * Data provider for testGetFieldType().
+   */
+  public function getFieldTypeProvider() {
+    return [
+      ['image', 'imagefield_widget'],
+      ['file', 'filefield_widget'],
+      ['file', 'x_widget']
+    ];
+  }
+
+  /**
+   * @covers ::getFieldType
+   * @dataProvider getFieldTypeProvider
+   */
+  public function testGetFieldType($expected_type, $widget_type, array $settings = []) {
+    $row = new Row();
+    $row->setSourceProperty('widget_type', $widget_type);
+    $row->setSourceProperty('global_settings', $settings);
+    $this->assertSame($expected_type, $this->plugin->getFieldType($row));
+  }
+
+}
diff --git a/core/modules/file/tests/src/Unit/Plugin/migrate/cckfield/d7/FileCckTest.php b/core/modules/file/tests/src/Unit/Plugin/migrate/cckfield/d7/FileCckTest.php
new file mode 100644
index 0000000..b393a32
--- /dev/null
+++ b/core/modules/file/tests/src/Unit/Plugin/migrate/cckfield/d7/FileCckTest.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Drupal\Tests\file\Unit\Plugin\migrate\cckfield\d7;
+
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate\Row;
+use Drupal\Tests\UnitTestCase;
+use Drupal\file\Plugin\migrate\cckfield\d7\FileField;
+use Prophecy\Argument;
+
+/**
+ * @coversDefaultClass \Drupal\file\Plugin\migrate\cckfield\d7\FileField
+ * @group file
+ */
+class FileCckTest extends UnitTestCase {
+
+  /**
+   * @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface
+   */
+  protected $plugin;
+
+  /**
+   * @var \Drupal\migrate\Plugin\MigrationInterface
+   */
+  protected $migration;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->plugin = new FileField([], 'file', []);
+
+    $migration = $this->prophesize(MigrationInterface::class);
+
+    // The plugin's processFieldValues() method will call
+    // mergeProcessOfProperty() and return nothing. So, in order to examine the
+    // process pipeline created by the plugin, we need to ensure that
+    // getProcess() always returns the last input to mergeProcessOfProperty().
+    $migration->mergeProcessOfProperty(Argument::type('string'), Argument::type('array'))
+      ->will(function($arguments) use ($migration) {
+        $migration->getProcess()->willReturn($arguments[1]);
+      });
+    $this->migration = $migration->reveal();
+  }
+
+  /**
+   * @covers ::processCckFieldValues
+   */
+  public function testProcessCckFieldValues() {
+    $this->plugin->processCckFieldValues($this->migration, 'somefieldname', []);
+
+    $expected = [
+      'plugin' => 'iterator',
+      'source' => 'somefieldname',
+      'process' => [
+        'target_id' => 'fid',
+        'display' => 'display',
+        'description' => 'description',
+      ],
+    ];
+    $this->assertSame($expected, $this->migration->getProcess());
+  }
+
+  /**
+   * Data provider for testGetFieldType().
+   */
+  public function getFieldTypeProvider() {
+    return [
+      ['image', 'imagefield_widget'],
+      ['file', 'filefield_widget'],
+      ['file', 'x_widget']
+    ];
+  }
+
+  /**
+   * @covers ::getFieldType
+   * @dataProvider getFieldTypeProvider
+   */
+  public function testGetFieldType($expected_type, $widget_type, array $settings = []) {
+    $row = new Row();
+    $row->setSourceProperty('widget_type', $widget_type);
+    $row->setSourceProperty('global_settings', $settings);
+    $this->assertSame($expected_type, $this->plugin->getFieldType($row));
+  }
+
+}
diff --git a/core/modules/file/tests/src/Unit/Plugin/migrate/cckfield/d7/ImageCckTest.php b/core/modules/file/tests/src/Unit/Plugin/migrate/cckfield/d7/ImageCckTest.php
new file mode 100644
index 0000000..368916b
--- /dev/null
+++ b/core/modules/file/tests/src/Unit/Plugin/migrate/cckfield/d7/ImageCckTest.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Drupal\Tests\file\Unit\Plugin\migrate\cckfield\d7;
+
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\Tests\UnitTestCase;
+use Drupal\file\Plugin\migrate\cckfield\d7\ImageField;
+use Prophecy\Argument;
+
+/**
+ * @coversDefaultClass \Drupal\file\Plugin\migrate\cckfield\d7\ImageField
+ * @group file
+ */
+class ImageCckTest extends UnitTestCase {
+
+  /**
+   * @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface
+   */
+  protected $plugin;
+
+  /**
+   * @var \Drupal\migrate\Plugin\MigrationInterface
+   */
+  protected $migration;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->plugin = new ImageField([], 'image', []);
+
+    $migration = $this->prophesize(MigrationInterface::class);
+
+    // The plugin's processFieldValues() method will call
+    // mergeProcessOfProperty() and return nothing. So, in order to examine the
+    // process pipeline created by the plugin, we need to ensure that
+    // getProcess() always returns the last input to mergeProcessOfProperty().
+    $migration->mergeProcessOfProperty(Argument::type('string'), Argument::type('array'))
+      ->will(function($arguments) use ($migration) {
+        $migration->getProcess()->willReturn($arguments[1]);
+      });
+    $this->migration = $migration->reveal();
+  }
+
+  /**
+   * @covers ::processCckFieldValues
+   */
+  public function testProcessCckFieldValues() {
+    $this->plugin->processCckFieldValues($this->migration, 'somefieldname', []);
+
+    $expected = [
+      'plugin' => 'iterator',
+      'source' => 'somefieldname',
+      'process' => [
+        'target_id' => 'fid',
+        'alt' => 'alt',
+        'title' => 'title',
+        'width' => 'width',
+        'height' => 'height',
+      ],
+    ];
+    $this->assertSame($expected, $this->migration->getProcess());
+  }
+
+}
diff --git a/core/modules/file/tests/src/Unit/Plugin/migrate/field/d6/FileFieldTest.php b/core/modules/file/tests/src/Unit/Plugin/migrate/field/d6/FileFieldTest.php
new file mode 100644
index 0000000..28d0842
--- /dev/null
+++ b/core/modules/file/tests/src/Unit/Plugin/migrate/field/d6/FileFieldTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Drupal\Tests\file\Unit\Plugin\migrate\field\d6;
+
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate\Row;
+use Drupal\Tests\UnitTestCase;
+use Drupal\file\Plugin\migrate\field\d6\FileField;
+use Prophecy\Argument;
+
+/**
+ * @coversDefaultClass \Drupal\file\Plugin\migrate\field\d6\FileField
+ * @group file
+ */
+class FileFieldTest extends UnitTestCase {
+
+  /**
+   * @var \Drupal\migrate_drupal\Plugin\MigrateFieldInterface
+   */
+  protected $plugin;
+
+  /**
+   * @var \Drupal\migrate\Plugin\MigrationInterface
+   */
+  protected $migration;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->plugin = new FileField([], 'file', []);
+
+    $migration = $this->prophesize(MigrationInterface::class);
+
+    // The plugin's processFieldValues() method will call
+    // mergeProcessOfProperty() and return nothing. So, in order to examine the
+    // process pipeline created by the plugin, we need to ensure that
+    // getProcess() always returns the last input to mergeProcessOfProperty().
+    $migration->mergeProcessOfProperty(Argument::type('string'), Argument::type('array'))
+      ->will(function($arguments) use ($migration) {
+        $migration->getProcess()->willReturn($arguments[1]);
+      });
+    $this->migration = $migration->reveal();
+  }
+
+  /**
+   * @covers ::processFieldValues
+   */
+  public function testProcessFieldValues() {
+    $this->plugin->processFieldValues($this->migration, 'somefieldname', []);
+
+    $expected = [
+      'plugin' => 'd6_field_file',
+      'source' => 'somefieldname',
+    ];
+    $this->assertSame($expected, $this->migration->getProcess());
+  }
+
+  /**
+   * Data provider for testGetFieldType().
+   */
+  public function getFieldTypeProvider() {
+    return [
+      ['image', 'imagefield_widget'],
+      ['file', 'filefield_widget'],
+      ['file', 'x_widget']
+    ];
+  }
+
+  /**
+   * @covers ::getFieldType
+   * @dataProvider getFieldTypeProvider
+   */
+  public function testGetFieldType($expected_type, $widget_type, array $settings = []) {
+    $row = new Row();
+    $row->setSourceProperty('widget_type', $widget_type);
+    $row->setSourceProperty('global_settings', $settings);
+    $this->assertSame($expected_type, $this->plugin->getFieldType($row));
+  }
+
+}
diff --git a/core/modules/file/tests/src/Unit/Plugin/migrate/field/d7/FileFieldTest.php b/core/modules/file/tests/src/Unit/Plugin/migrate/field/d7/FileFieldTest.php
new file mode 100644
index 0000000..15d86a1
--- /dev/null
+++ b/core/modules/file/tests/src/Unit/Plugin/migrate/field/d7/FileFieldTest.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Drupal\Tests\file\Unit\Plugin\migrate\field\d7;
+
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate\Row;
+use Drupal\Tests\UnitTestCase;
+use Drupal\file\Plugin\migrate\field\d7\FileField;
+use Prophecy\Argument;
+
+/**
+ * @coversDefaultClass \Drupal\file\Plugin\migrate\field\d7\FileField
+ * @group file
+ */
+class FileFieldTest extends UnitTestCase {
+
+  /**
+   * @var \Drupal\migrate_drupal\Plugin\MigrateFieldInterface
+   */
+  protected $plugin;
+
+  /**
+   * @var \Drupal\migrate\Plugin\MigrationInterface
+   */
+  protected $migration;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->plugin = new FileField([], 'file', []);
+
+    $migration = $this->prophesize(MigrationInterface::class);
+
+    // The plugin's processFieldValues() method will call
+    // mergeProcessOfProperty() and return nothing. So, in order to examine the
+    // process pipeline created by the plugin, we need to ensure that
+    // getProcess() always returns the last input to mergeProcessOfProperty().
+    $migration->mergeProcessOfProperty(Argument::type('string'), Argument::type('array'))
+      ->will(function($arguments) use ($migration) {
+        $migration->getProcess()->willReturn($arguments[1]);
+      });
+    $this->migration = $migration->reveal();
+  }
+
+  /**
+   * @covers ::processFieldValues
+   */
+  public function testProcessFieldValues() {
+    $this->plugin->processFieldValues($this->migration, 'somefieldname', []);
+
+    $expected = [
+      'plugin' => 'iterator',
+      'source' => 'somefieldname',
+      'process' => [
+        'target_id' => 'fid',
+        'display' => 'display',
+        'description' => 'description',
+      ],
+    ];
+    $this->assertSame($expected, $this->migration->getProcess());
+  }
+
+  /**
+   * Data provider for testGetFieldType().
+   */
+  public function getFieldTypeProvider() {
+    return [
+      ['image', 'imagefield_widget'],
+      ['file', 'filefield_widget'],
+      ['file', 'x_widget']
+    ];
+  }
+
+  /**
+   * @covers ::getFieldType
+   * @dataProvider getFieldTypeProvider
+   */
+  public function testGetFieldType($expected_type, $widget_type, array $settings = []) {
+    $row = new Row();
+    $row->setSourceProperty('widget_type', $widget_type);
+    $row->setSourceProperty('global_settings', $settings);
+    $this->assertSame($expected_type, $this->plugin->getFieldType($row));
+  }
+
+}
diff --git a/core/modules/file/tests/src/Unit/Plugin/migrate/field/d7/ImageFieldTest.php b/core/modules/file/tests/src/Unit/Plugin/migrate/field/d7/ImageFieldTest.php
new file mode 100644
index 0000000..61fda03
--- /dev/null
+++ b/core/modules/file/tests/src/Unit/Plugin/migrate/field/d7/ImageFieldTest.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Drupal\Tests\file\Unit\Plugin\migrate\field\d7;
+
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\Tests\UnitTestCase;
+use Drupal\file\Plugin\migrate\field\d7\ImageField;
+use Prophecy\Argument;
+
+/**
+ * @coversDefaultClass \Drupal\file\Plugin\migrate\field\d7\ImageField
+ * @group file
+ */
+class ImageFieldTest extends UnitTestCase {
+
+  /**
+   * @var \Drupal\migrate_drupal\Plugin\MigrateFieldInterface
+   */
+  protected $plugin;
+
+  /**
+   * @var \Drupal\migrate\Plugin\MigrationInterface
+   */
+  protected $migration;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->plugin = new ImageField([], 'image', []);
+
+    $migration = $this->prophesize(MigrationInterface::class);
+
+    // The plugin's processFieldValues() method will call
+    // mergeProcessOfProperty() and return nothing. So, in order to examine the
+    // process pipeline created by the plugin, we need to ensure that
+    // getProcess() always returns the last input to mergeProcessOfProperty().
+    $migration->mergeProcessOfProperty(Argument::type('string'), Argument::type('array'))
+      ->will(function($arguments) use ($migration) {
+        $migration->getProcess()->willReturn($arguments[1]);
+      });
+    $this->migration = $migration->reveal();
+  }
+
+  /**
+   * @covers ::processFieldValues
+   */
+  public function testProcessFieldValues() {
+    $this->plugin->processFieldValues($this->migration, 'somefieldname', []);
+
+    $expected = [
+      'plugin' => 'iterator',
+      'source' => 'somefieldname',
+      'process' => [
+        'target_id' => 'fid',
+        'alt' => 'alt',
+        'title' => 'title',
+        'width' => 'width',
+        'height' => 'height',
+      ],
+    ];
+    $this->assertSame($expected, $this->migration->getProcess());
+  }
+
+}
diff --git a/core/modules/file/tests/src/Unit/Plugin/migrate/process/d6/FieldFileTest.php b/core/modules/file/tests/src/Unit/Plugin/migrate/process/d6/FieldFileTest.php
new file mode 100644
index 0000000..e57821c
--- /dev/null
+++ b/core/modules/file/tests/src/Unit/Plugin/migrate/process/d6/FieldFileTest.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\Tests\file\Unit\Plugin\migrate\process\d6;
+
+use Drupal\file\Plugin\migrate\process\d6\FieldFile;
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate\MigrateExecutableInterface;
+use Drupal\migrate\Plugin\MigrateProcessInterface;
+use Drupal\migrate\Row;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @group file
+ */
+class FieldFileTest extends UnitTestCase {
+
+  /**
+   * Tests that alt and title attributes are included in transformed values.
+   */
+  public function testTransformAltTitle() {
+    $executable = $this->prophesize(MigrateExecutableInterface::class)->reveal();
+    $row = $this->prophesize(Row::class)->reveal();
+    $migration = $this->prophesize(MigrationInterface::class)->reveal();
+
+    $migration_plugin = $this->prophesize(MigrateProcessInterface::class);
+    $migration_plugin->transform(1, $executable, $row, 'foo')->willReturn(1);
+
+    $plugin = new FieldFile([], 'd6_file', [], $migration, $migration_plugin->reveal());
+
+    $options = [
+      'alt' => 'Foobaz',
+      'title' => 'Wambooli',
+    ];
+    $value = [
+      'fid' => 1,
+      'list' => TRUE,
+      'data' => serialize($options),
+    ];
+
+    $transformed = $plugin->transform($value, $executable, $row, 'foo');
+    $expected = [
+      'target_id' => 1,
+      'display' => TRUE,
+      'description' => '',
+      'alt' => 'Foobaz',
+      'title' => 'Wambooli',
+    ];
+    $this->assertSame($expected, $transformed);
+  }
+
+}
diff --git a/core/modules/link/src/Plugin/migrate/cckfield/LinkField.php b/core/modules/link/src/Plugin/migrate/cckfield/LinkField.php
new file mode 100644
index 0000000..314e8df
--- /dev/null
+++ b/core/modules/link/src/Plugin/migrate/cckfield/LinkField.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Drupal\link\Plugin\migrate\cckfield;
+
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate_drupal\Plugin\migrate\cckfield\CckFieldPluginBase;
+
+/**
+ * @MigrateCckField(
+ *   id = "link",
+ *   core = {6},
+ *   type_map = {
+ *     "link_field" = "link"
+ *   }
+ * )
+ */
+class LinkField extends CckFieldPluginBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldFormatterMap() {
+    // See d6_field_formatter_settings.yml and CckFieldPluginBase
+    // processFieldFormatter().
+    return [
+      'default' => 'link',
+      'plain' => 'link',
+      'absolute' => 'link',
+      'title_plain' => 'link',
+      'url' => 'link',
+      'short' => 'link',
+      'label' => 'link',
+      'separate' => 'link_separate',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processCckFieldValues(MigrationInterface $migration, $field_name, $data) {
+    $process = [
+      'plugin' => 'd6_cck_link',
+      'source' => $field_name,
+    ];
+    $migration->mergeProcessOfProperty($field_name, $process);
+  }
+
+}
diff --git a/core/modules/link/src/Plugin/migrate/cckfield/d7/LinkField.php b/core/modules/link/src/Plugin/migrate/cckfield/d7/LinkField.php
new file mode 100644
index 0000000..5de568d
--- /dev/null
+++ b/core/modules/link/src/Plugin/migrate/cckfield/d7/LinkField.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Drupal\link\Plugin\migrate\cckfield\d7;
+
+use Drupal\link\Plugin\migrate\cckfield\LinkField as D6LinkField;
+use Drupal\migrate\Plugin\MigrationInterface;
+
+/**
+ * @MigrateCckField(
+ *   id = "link_field",
+ *   core = {7},
+ *   type_map = {
+ *     "link_field" = "link"
+ *   }
+ * )
+ *
+ * This plugin provides the exact same functionality as the Drupal 6 "link"
+ * plugin with the exception that the plugin ID "link_field" is used in the
+ * field type map.
+ */
+class LinkField extends D6LinkField {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldWidgetMap() {
+    // By default, use the plugin ID for the widget types.
+    return ['link_field' => 'link_default'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processFieldInstance(MigrationInterface $migration) {
+    $process = [
+      'plugin' => 'static_map',
+      'source' => 'instance_settings/title',
+      'bypass' => TRUE,
+      'map' => [
+        'disabled' => DRUPAL_DISABLED,
+        'optional' => DRUPAL_OPTIONAL,
+        'required' => DRUPAL_REQUIRED,
+      ],
+    ];
+    $migration->mergeProcessOfProperty('settings/title', $process);
+  }
+
+}
diff --git a/core/modules/migrate_drupal/tests/src/Kernel/MigrateCckFieldPluginManagerTest.php b/core/modules/migrate_drupal/tests/src/Kernel/MigrateCckFieldPluginManagerTest.php
index 9e30795..7eb68ba 100644
--- a/core/modules/migrate_drupal/tests/src/Kernel/MigrateCckFieldPluginManagerTest.php
+++ b/core/modules/migrate_drupal/tests/src/Kernel/MigrateCckFieldPluginManagerTest.php
@@ -22,28 +22,20 @@ class MigrateCckFieldPluginManagerTest extends MigrateDrupalTestBase {
   public function testPluginSelection() {
     $plugin_manager = \Drupal::service('plugin.manager.migrate.cckfield');
 
-    $plugin_id = $plugin_manager->getPluginIdFromFieldType('filefield', ['core' => 6]);
-    $this->assertIdentical('Drupal\\file\\Plugin\\migrate\\cckfield\\d6\\FileField', get_class($plugin_manager->createInstance($plugin_id, ['core' => 6])));
+    $this->assertSame('d6_file', $plugin_manager->getPluginIdFromFieldType('file', ['core' => 6]));
 
     try {
       // If this test passes, getPluginIdFromFieldType will raise a
       // PluginNotFoundException and we'll never reach fail().
-      $plugin_manager->getPluginIdFromFieldType('filefield', ['core' => 7]);
+      $plugin_manager->getPluginIdFromFieldType('d6_file', ['core' => 7]);
       $this->fail('Expected Drupal\Component\Plugin\Exception\PluginNotFoundException.');
     }
     catch (PluginNotFoundException $e) {
-      $this->assertIdentical($e->getMessage(), "Plugin ID 'filefield' was not found.");
+      $this->assertSame($e->getMessage(), "Plugin ID 'd6_file' was not found.");
     }
 
-    $this->assertIdentical('image', $plugin_manager->getPluginIdFromFieldType('image', ['core' => 7]));
-    $this->assertIdentical('file', $plugin_manager->getPluginIdFromFieldType('file', ['core' => 7]));
-    $this->assertIdentical('d6_file', $plugin_manager->getPluginIdFromFieldType('file', ['core' => 6]));
-
-    $this->assertIdentical('text', $plugin_manager->getPluginIdFromFieldType('text', ['core' => 6]));
-    $this->assertIdentical('text', $plugin_manager->getPluginIdFromFieldType('text', ['core' => 7]));
-
     // Test fallback when no core version is specified.
-    $this->assertIdentical('d6_no_core_version_specified', $plugin_manager->getPluginIdFromFieldType('d6_no_core_version_specified', ['core' => 6]));
+    $this->assertSame('d6_no_core_version_specified', $plugin_manager->getPluginIdFromFieldType('d6_no_core_version_specified', ['core' => 6]));
 
     try {
       // If this test passes, getPluginIdFromFieldType will raise a
@@ -52,7 +44,7 @@ public function testPluginSelection() {
       $this->fail('Expected Drupal\Component\Plugin\Exception\PluginNotFoundException.');
     }
     catch (PluginNotFoundException $e) {
-      $this->assertIdentical($e->getMessage(), "Plugin ID 'd6_no_core_version_specified' was not found.");
+      $this->assertSame($e->getMessage(), "Plugin ID 'd6_no_core_version_specified' was not found.");
     }
   }
 
diff --git a/core/modules/migrate_drupal/tests/src/Kernel/MigrateFieldPluginManagerTest.php b/core/modules/migrate_drupal/tests/src/Kernel/MigrateFieldPluginManagerTest.php
index 7c3c5a4..7aa2c0f 100644
--- a/core/modules/migrate_drupal/tests/src/Kernel/MigrateFieldPluginManagerTest.php
+++ b/core/modules/migrate_drupal/tests/src/Kernel/MigrateFieldPluginManagerTest.php
@@ -14,7 +14,7 @@ class MigrateFieldPluginManagerTest extends MigrateDrupalTestBase {
   /**
    * {@inheritdoc}
    */
-  public static $modules = ['system', 'user', 'field', 'migrate_drupal', 'options', 'file', 'text', 'migrate_field_plugin_manager_test'];
+  public static $modules = ['system', 'user', 'field', 'migrate_drupal', 'options', 'file', 'text', 'link', 'migrate_field_plugin_manager_test'];
 
   /**
    * Tests that the correct MigrateField plugins are used.
@@ -32,7 +32,13 @@ public function testPluginSelection() {
       $this->assertIdentical($e->getMessage(), "Plugin ID 'filefield' was not found.");
     }
 
+    $this->assertIdentical('link', $plugin_manager->getPluginIdFromFieldType('link', ['core' => 6]));
+    $this->assertIdentical('link_field', $plugin_manager->getPluginIdFromFieldType('link_field', ['core' => 7]));
+    $this->assertIdentical('image', $plugin_manager->getPluginIdFromFieldType('image', ['core' => 7]));
+    $this->assertIdentical('file', $plugin_manager->getPluginIdFromFieldType('file', ['core' => 7]));
     $this->assertIdentical('d6_file', $plugin_manager->getPluginIdFromFieldType('file', ['core' => 6]));
+    $this->assertIdentical('text', $plugin_manager->getPluginIdFromFieldType('text', ['core' => 6]));
+    $this->assertIdentical('text', $plugin_manager->getPluginIdFromFieldType('text', ['core' => 7]));
 
     // Test fallback when no core version is specified.
     $this->assertIdentical('d6_no_core_version_specified', $plugin_manager->getPluginIdFromFieldType('d6_no_core_version_specified', ['core' => 6]));
diff --git a/core/modules/node/src/Plugin/migrate/source/d6/Node.php b/core/modules/node/src/Plugin/migrate/source/d6/Node.php
index ffa2659..3fa0ada 100644
--- a/core/modules/node/src/Plugin/migrate/source/d6/Node.php
+++ b/core/modules/node/src/Plugin/migrate/source/d6/Node.php
@@ -176,24 +176,24 @@ public function prepareRow(Row $row) {
   }
 
   /**
-   * Gets CCK field values for a node.
+   * Gets field values for a node.
    *
    * @param \Drupal\migrate\Row $node
    *   The node.
    *
    * @return array
-   *   CCK field values, keyed by field name.
+   *   Field values, keyed by field name.
    */
   protected function getFieldValues(Row $node) {
     $values = [];
     foreach ($this->getFieldInfo($node->getSourceProperty('type')) as $field => $info) {
-      $values[$field] = $this->getCckData($info, $node);
+      $values[$field] = $this->getFieldData($info, $node);
     }
     return $values;
   }
 
   /**
-   * Gets CCK field and instance definitions from the database.
+   * Gets field and instance definitions from the database.
    *
    * @param string $node_type
    *   The node type for which to get field info.
@@ -205,7 +205,7 @@ protected function getFieldInfo($node_type) {
     if (!isset($this->fieldInfo)) {
       $this->fieldInfo = [];
 
-      // Query the database directly for all CCK field info.
+      // Query the database directly for all field info.
       $query = $this->select('content_node_field_instance', 'cnfi');
       $query->join('content_node_field', 'cnf', 'cnf.field_name = cnfi.field_name');
       $query->fields('cnfi');
@@ -230,7 +230,7 @@ protected function getFieldInfo($node_type) {
   }
 
   /**
-   * Retrieves raw CCK field data for a node.
+   * Retrieves raw field data for a node.
    *
    * @param array $field
    *   A field and instance definition from getFieldInfo().
@@ -240,7 +240,7 @@ protected function getFieldInfo($node_type) {
    * @return array
    *   The field values, keyed by delta.
    */
-  protected function getCckData(array $field, Row $node) {
+  protected function getFieldData(array $field, Row $node) {
     $field_table = 'content_' . $field['field_name'];
     $node_table = 'content_type_' . $node->getSourceProperty('type');
 
@@ -276,10 +276,9 @@ protected function getCckData(array $field, Row $node) {
 
       return $query
         // This call to isNotNull() is a kludge which relies on the convention
-        // that CCK field schemas usually define their most important
-        // column first. A better way would be to allow cckfield plugins to
-        // alter the query directly before it's run, but this will do for
-        // the time being.
+        // that field schemas usually define their most important column first.
+        // A better way would be to allow field plugins to alter the query
+        // directly before it's run, but this will do for the time being.
         ->isNotNull($field['field_name'] . '_' . $columns[0])
         ->condition('nid', $node->getSourceProperty('nid'))
         ->condition('vid', $node->getSourceProperty('vid'))
@@ -292,6 +291,24 @@ protected function getCckData(array $field, Row $node) {
   }
 
   /**
+   * Retrieves raw field data for a node.
+   *
+   * @deprecated in Drupal 8.2.x, to be removed in Drupal 9.0.x. Use
+   *   getFieldData() instead.
+   *
+   * @param array $field
+   *   A field and instance definition from getFieldInfo().
+   * @param \Drupal\migrate\Row $node
+   *   The node.
+   *
+   * @return array
+   *   The field values, keyed by delta.
+   */
+  protected function getCckData(array $field, Row $node) {
+    return $this->getFieldData($field, $node);
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function getIds() {
diff --git a/core/modules/taxonomy/src/Plugin/migrate/field/TaxonomyTermReference.php b/core/modules/taxonomy/src/Plugin/migrate/field/TaxonomyTermReference.php
new file mode 100644
index 0000000..5574a44
--- /dev/null
+++ b/core/modules/taxonomy/src/Plugin/migrate/field/TaxonomyTermReference.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Drupal\taxonomy\Plugin\migrate\field;
+
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase;
+
+/**
+ * @MigrateField(
+ *   id = "taxonomy_term_reference",
+ *   type_map = {
+ *     "taxonomy_term_reference" = "entity_reference"
+ *   },
+ *   core = {6,7}
+ * )
+ */
+class TaxonomyTermReference extends FieldPluginBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldFormatterMap() {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processFieldValues(MigrationInterface $migration, $field_name, $data) {
+    $process = [
+      'plugin' => 'iterator',
+      'source' => $field_name,
+      'process' => [
+        'target_id' => 'tid',
+      ],
+    ];
+    $migration->setProcessOfProperty($field_name, $process);
+  }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Unit/Plugin/migrate/cckfield/TaxonomyTermReferenceCckTest.php b/core/modules/taxonomy/tests/src/Unit/Plugin/migrate/cckfield/TaxonomyTermReferenceCckTest.php
new file mode 100644
index 0000000..c57857c
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Unit/Plugin/migrate/cckfield/TaxonomyTermReferenceCckTest.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Drupal\Tests\taxonomy\Unit\Plugin\migrate\cckfield;
+
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\Tests\UnitTestCase;
+use Drupal\taxonomy\Plugin\migrate\cckfield\TaxonomyTermReference;
+use Prophecy\Argument;
+
+/**
+ * @coversDefaultClass \Drupal\taxonomy\Plugin\migrate\cckfield\TaxonomyTermReference
+ * @group taxonomy
+ */
+class TaxonomyTermReferenceCckTest extends UnitTestCase {
+
+  /**
+   * @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface
+   */
+  protected $plugin;
+
+  /**
+   * @var \Drupal\migrate\Plugin\MigrationInterface
+   */
+  protected $migration;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->plugin = new TaxonomyTermReference([], 'taxonomy', []);
+
+    $migration = $this->prophesize(MigrationInterface::class);
+
+    // The plugin's processCckFieldValues() method will call
+    // setProcessOfProperty() and return nothing. So, in order to examine the
+    // process pipeline created by the plugin, we need to ensure that
+    // getProcess() always returns the last input to setProcessOfProperty().
+    $migration->setProcessOfProperty(Argument::type('string'), Argument::type('array'))
+      ->will(function($arguments) use ($migration) {
+        $migration->getProcess()->willReturn($arguments[1]);
+      });
+
+    $this->migration = $migration->reveal();
+  }
+
+  /**
+   * @covers ::processCckFieldValues
+   */
+  public function testProcessCckFieldValues() {
+    $this->plugin->processFieldValues($this->migration, 'somefieldname', []);
+
+    $expected = [
+      'plugin' => 'iterator',
+      'source' => 'somefieldname',
+      'process' => [
+        'target_id' => 'tid',
+      ],
+    ];
+    $this->assertSame($expected, $this->migration->getProcess());
+  }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Unit/Plugin/migrate/field/TaxonomyTermReferenceFieldTest.php b/core/modules/taxonomy/tests/src/Unit/Plugin/migrate/field/TaxonomyTermReferenceFieldTest.php
new file mode 100644
index 0000000..de88d7d
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Unit/Plugin/migrate/field/TaxonomyTermReferenceFieldTest.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Drupal\Tests\taxonomy\Unit\Plugin\migrate\field;
+
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\Tests\UnitTestCase;
+use Drupal\taxonomy\Plugin\migrate\field\TaxonomyTermReference;
+use Prophecy\Argument;
+
+/**
+ * @coversDefaultClass \Drupal\taxonomy\Plugin\migrate\field\TaxonomyTermReference
+ * @group taxonomy
+ */
+class TaxonomyTermReferenceFieldTest extends UnitTestCase {
+
+  /**
+   * @var \Drupal\migrate_drupal\Plugin\MigrateFieldInterface
+   */
+  protected $plugin;
+
+  /**
+   * @var \Drupal\migrate\Plugin\MigrationInterface
+   */
+  protected $migration;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->plugin = new TaxonomyTermReference([], 'taxonomy', []);
+
+    $migration = $this->prophesize(MigrationInterface::class);
+
+    // The plugin's processFieldValues() method will call
+    // setProcessOfProperty() and return nothing. So, in order to examine the
+    // process pipeline created by the plugin, we need to ensure that
+    // getProcess() always returns the last input to setProcessOfProperty().
+    $migration->setProcessOfProperty(Argument::type('string'), Argument::type('array'))
+      ->will(function($arguments) use ($migration) {
+        $migration->getProcess()->willReturn($arguments[1]);
+      });
+
+    $this->migration = $migration->reveal();
+  }
+
+  /**
+   * @covers ::processFieldValues
+   */
+  public function testProcessFieldValues() {
+    $this->plugin->processFieldValues($this->migration, 'somefieldname', []);
+
+    $expected = [
+      'plugin' => 'iterator',
+      'source' => 'somefieldname',
+      'process' => [
+        'target_id' => 'tid',
+      ],
+    ];
+    $this->assertSame($expected, $this->migration->getProcess());
+  }
+
+}
diff --git a/core/modules/text/src/Plugin/migrate/field/TextField.php b/core/modules/text/src/Plugin/migrate/field/TextField.php
new file mode 100644
index 0000000..dabaa72
--- /dev/null
+++ b/core/modules/text/src/Plugin/migrate/field/TextField.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace Drupal\text\Plugin\migrate\field;
+
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate\Row;
+use Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase;
+
+/**
+ * @MigrateField(
+ *   id = "text",
+ *   type_map = {
+ *     "text" = "text",
+ *     "text_long" = "text_long",
+ *     "text_with_summary" = "text_with_summary"
+ *   },
+ *   core = {6,7}
+ * )
+ */
+class TextField extends FieldPluginBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldWidgetMap() {
+    return [
+      'text_textfield' => 'text_textfield',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldFormatterMap() {
+    return [
+      'default' => 'text_default',
+      'trimmed' => 'text_trimmed',
+      'plain' => 'basic_string',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processFieldValues(MigrationInterface $migration, $field_name, $field_info) {
+    if ($field_info['widget_type'] == 'optionwidgets_onoff') {
+      $process = [
+        'value' => [
+          'plugin' => 'static_map',
+          'source' => 'value',
+          'default_value' => 0,
+        ],
+      ];
+
+      $checked_value = explode("\n", $field_info['global_settings']['allowed_values'])[1];
+      if (strpos($checked_value, '|') !== FALSE) {
+        $checked_value = substr($checked_value, 0, strpos($checked_value, '|'));
+      }
+      $process['value']['map'][$checked_value] = 1;
+    }
+    else {
+      // See \Drupal\migrate_drupal\Plugin\migrate\source\d6\User::baseFields(),
+      // signature_format for an example of the YAML that represents this
+      // process array.
+      $process = [
+        'value' => 'value',
+        'format' => [
+          [
+            'plugin' => 'static_map',
+            'bypass' => TRUE,
+            'source' => 'format',
+            'map' => [0 => NULL],
+          ],
+          [
+            'plugin' => 'skip_on_empty',
+            'method' => 'process',
+          ],
+          [
+            'plugin' => 'migration',
+            'migration' => [
+              'd6_filter_format',
+              'd7_filter_format',
+            ],
+            'source' => 'format',
+          ],
+        ],
+      ];
+    }
+
+    $process = [
+      'plugin' => 'iterator',
+      'source' => $field_name,
+      'process' => $process,
+    ];
+    $migration->setProcessOfProperty($field_name, $process);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldType(Row $row) {
+    $widget_type = $row->getSourceProperty('widget_type');
+    $settings = $row->getSourceProperty('global_settings');
+
+    if ($widget_type == 'text_textfield') {
+      $field_type = $settings['text_processing'] ? 'text' : 'string';
+      if (empty($settings['max_length']) || $settings['max_length'] > 255) {
+        $field_type .= '_long';
+      }
+      return $field_type;
+    }
+
+    if ($widget_type == 'text_textarea') {
+      $field_type = $settings['text_processing'] ? 'text_long' : 'string_long';
+      return $field_type;
+    }
+
+    switch ($widget_type) {
+      case 'optionwidgets_buttons':
+      case 'optionwidgets_select':
+        return 'list_string';
+      case 'optionwidgets_onoff':
+        return 'boolean';
+      default:
+        return parent::getFieldType($row);
+    }
+  }
+
+}
diff --git a/core/modules/text/tests/src/Unit/Migrate/TextCckTest.php b/core/modules/text/tests/src/Unit/Migrate/TextCckTest.php
new file mode 100644
index 0000000..dace2f9
--- /dev/null
+++ b/core/modules/text/tests/src/Unit/Migrate/TextCckTest.php
@@ -0,0 +1,170 @@
+<?php
+
+namespace Drupal\Tests\text\Unit\Migrate;
+
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate\Row;
+use Drupal\Tests\UnitTestCase;
+use Drupal\text\Plugin\migrate\cckfield\TextField;
+use Prophecy\Argument;
+
+/**
+ * @coversDefaultClass \Drupal\text\Plugin\migrate\cckfield\TextField
+ * @group text
+ */
+class TextCckTest extends UnitTestCase {
+
+  /**
+   * @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface
+   */
+  protected $plugin;
+
+  /**
+   * @var \Drupal\migrate\Plugin\MigrationInterface
+   */
+  protected $migration;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->plugin = new TextField([], 'text', []);
+
+    $migration = $this->prophesize(MigrationInterface::class);
+
+    // The plugin's processCckFieldValues() method will call
+    // setProcessOfProperty() and return nothing. So, in order to examine the
+    // process pipeline created by the plugin, we need to ensure that
+    // getProcess() always returns the last input to setProcessOfProperty().
+    $migration->setProcessOfProperty(Argument::type('string'), Argument::type('array'))
+      ->will(function($arguments) use ($migration) {
+        $migration->getProcess()->willReturn($arguments[1]);
+      });
+
+    $this->migration = $migration->reveal();
+  }
+
+  /**
+   * @covers ::processCckFieldValues
+   */
+  public function testProcessFilteredTextFieldValues() {
+    $field_info = [
+      'widget_type' => 'text_textfield',
+    ];
+    $this->plugin->processCckFieldValues($this->migration, 'body', $field_info);
+
+    $process = $this->migration->getProcess();
+    $this->assertSame('iterator', $process['plugin']);
+    $this->assertSame('body', $process['source']);
+    $this->assertSame('value', $process['process']['value']);
+
+    // Ensure that filter format IDs will be looked up in the filter format
+    // migrations.
+    $lookup = $process['process']['format'][2];
+    $this->assertSame('migration', $lookup['plugin']);
+    $this->assertContains('d6_filter_format', $lookup['migration']);
+    $this->assertContains('d7_filter_format', $lookup['migration']);
+    $this->assertSame('format', $lookup['source']);
+  }
+
+  /**
+   * @covers ::processCckFieldValues
+   */
+  public function testProcessBooleanTextImplicitValues() {
+    $info = [
+      'widget_type' => 'optionwidgets_onoff',
+      'global_settings' => [
+        'allowed_values' => "foo\nbar",
+      ]
+    ];
+    $this->plugin->processCckFieldValues($this->migration, 'field', $info);
+
+    $expected = [
+      'value' => [
+        'plugin' => 'static_map',
+        'source' => 'value',
+        'default_value' => 0,
+        'map' => [
+          'bar' => 1,
+        ],
+      ],
+    ];
+    $this->assertSame($expected, $this->migration->getProcess()['process']);
+  }
+
+  /**
+   * @covers ::processCckFieldValues
+   */
+  public function testProcessBooleanTextExplicitValues() {
+    $info = [
+      'widget_type' => 'optionwidgets_onoff',
+      'global_settings' => [
+        'allowed_values' => "foo|Foo\nbaz|Baz",
+      ]
+    ];
+    $this->plugin->processCckFieldValues($this->migration, 'field', $info);
+
+    $expected = [
+      'value' => [
+        'plugin' => 'static_map',
+        'source' => 'value',
+        'default_value' => 0,
+        'map' => [
+          'baz' => 1,
+        ],
+      ],
+    ];
+    $this->assertSame($expected, $this->migration->getProcess()['process']);
+  }
+
+  /**
+   * Data provider for testGetFieldType().
+   */
+  public function getFieldTypeProvider() {
+    return [
+      ['string_long', 'text_textfield', [
+        'text_processing' => FALSE,
+      ]],
+      ['string', 'text_textfield', [
+        'text_processing' => FALSE,
+        'max_length' => 128,
+      ]],
+      ['string_long', 'text_textfield', [
+        'text_processing' => FALSE,
+        'max_length' => 4096,
+      ]],
+      ['text_long', 'text_textfield', [
+        'text_processing' => TRUE,
+      ]],
+      ['text', 'text_textfield', [
+        'text_processing' => TRUE,
+        'max_length' => 128,
+      ]],
+      ['text_long', 'text_textfield', [
+        'text_processing' => TRUE,
+        'max_length' => 4096,
+      ]],
+      ['list_string', 'optionwidgets_buttons'],
+      ['list_string', 'optionwidgets_select'],
+      ['boolean', 'optionwidgets_onoff'],
+      ['text_long', 'text_textarea', [
+        'text_processing' => TRUE,
+      ]],
+      ['string_long', 'text_textarea', [
+        'text_processing' => FALSE,
+      ]],
+      [NULL, 'undefined'],
+    ];
+  }
+
+  /**
+   * @covers ::getFieldType
+   * @dataProvider getFieldTypeProvider
+   */
+  public function testGetFieldType($expected_type, $widget_type, array $settings = []) {
+    $row = new Row(['widget_type' => $widget_type], ['widget_type' => []]);
+    $row->setSourceProperty('global_settings', $settings);
+    $this->assertSame($expected_type, $this->plugin->getFieldType($row));
+  }
+
+}
diff --git a/core/modules/text/tests/src/Unit/Migrate/TextFieldTest.php b/core/modules/text/tests/src/Unit/Migrate/TextFieldTest.php
index 6d433ea..88effde 100644
--- a/core/modules/text/tests/src/Unit/Migrate/TextFieldTest.php
+++ b/core/modules/text/tests/src/Unit/Migrate/TextFieldTest.php
@@ -5,17 +5,17 @@
 use Drupal\migrate\Plugin\MigrationInterface;
 use Drupal\migrate\Row;
 use Drupal\Tests\UnitTestCase;
-use Drupal\text\Plugin\migrate\cckfield\TextField;
+use Drupal\text\Plugin\migrate\field\TextField;
 use Prophecy\Argument;
 
 /**
- * @coversDefaultClass \Drupal\text\Plugin\migrate\cckfield\TextField
+ * @coversDefaultClass \Drupal\text\Plugin\migrate\field\TextField
  * @group text
  */
 class TextFieldTest extends UnitTestCase {
 
   /**
-   * @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface
+   * @var \Drupal\migrate_drupal\Plugin\MigrateFieldInterface
    */
   protected $plugin;
 
@@ -32,7 +32,7 @@ protected function setUp() {
 
     $migration = $this->prophesize(MigrationInterface::class);
 
-    // The plugin's processCckFieldValues() method will call
+    // The plugin's processFieldValues() method will call
     // setProcessOfProperty() and return nothing. So, in order to examine the
     // process pipeline created by the plugin, we need to ensure that
     // getProcess() always returns the last input to setProcessOfProperty().
@@ -45,13 +45,13 @@ protected function setUp() {
   }
 
   /**
-   * @covers ::processCckFieldValues
+   * @covers ::processFieldValues
    */
   public function testProcessFilteredTextFieldValues() {
     $field_info = [
       'widget_type' => 'text_textfield',
     ];
-    $this->plugin->processCckFieldValues($this->migration, 'body', $field_info);
+    $this->plugin->processFieldValues($this->migration, 'body', $field_info);
 
     $process = $this->migration->getProcess();
     $this->assertSame('iterator', $process['plugin']);
@@ -68,7 +68,7 @@ public function testProcessFilteredTextFieldValues() {
   }
 
   /**
-   * @covers ::processCckFieldValues
+   * @covers ::processFieldValues
    */
   public function testProcessBooleanTextImplicitValues() {
     $info = [
@@ -77,7 +77,7 @@ public function testProcessBooleanTextImplicitValues() {
         'allowed_values' => "foo\nbar",
       ]
     ];
-    $this->plugin->processCckFieldValues($this->migration, 'field', $info);
+    $this->plugin->processFieldValues($this->migration, 'field', $info);
 
     $expected = [
       'value' => [
@@ -93,7 +93,7 @@ public function testProcessBooleanTextImplicitValues() {
   }
 
   /**
-   * @covers ::processCckFieldValues
+   * @covers ::processFieldValues
    */
   public function testProcessBooleanTextExplicitValues() {
     $info = [
@@ -102,7 +102,7 @@ public function testProcessBooleanTextExplicitValues() {
         'allowed_values' => "foo|Foo\nbaz|Baz",
       ]
     ];
-    $this->plugin->processCckFieldValues($this->migration, 'field', $info);
+    $this->plugin->processFieldValues($this->migration, 'field', $info);
 
     $expected = [
       'value' => [
